Skip to content

Commit 581d46b

Browse files
authored
fix(storage): disable self-signed JWT authentication (#6238)
My tests were wrong, this does not work in production. Fortunately they were not used unless the service account keyfile was manually loaded.
1 parent 6bc6b50 commit 581d46b

File tree

4 files changed

+166
-17
lines changed

4 files changed

+166
-17
lines changed

google/cloud/storage/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ if (BUILD_TESTING)
2323

2424
set(storage_examples
2525
# cmake-format: sort
26+
storage_auth_samples.cc
2627
storage_bucket_acl_samples.cc
2728
storage_bucket_cors_samples.cc
2829
storage_bucket_default_kms_key_samples.cc
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/storage/client.h"
16+
#include "google/cloud/storage/examples/storage_examples_common.h"
17+
#include "google/cloud/storage/oauth2/google_credentials.h"
18+
#include "google/cloud/storage/parallel_upload.h"
19+
#include "google/cloud/storage/well_known_parameters.h"
20+
#include "google/cloud/internal/getenv.h"
21+
#include <iostream>
22+
#include <map>
23+
#include <string>
24+
#include <thread>
25+
26+
namespace {
27+
28+
void PerformSomeOperations(google::cloud::storage::Client client,
29+
std::string const& bucket_name,
30+
std::string const& object_name) {
31+
namespace gcs = google::cloud::storage;
32+
auto constexpr kText = "The quick brown fox jumps over the lazy dog\n";
33+
34+
auto object = client.InsertObject(bucket_name, object_name, kText).value();
35+
for (auto&& o : client.ListObjects(bucket_name)) {
36+
if (!o) throw std::runtime_error(o.status().message());
37+
if (o->name() == object_name) break;
38+
}
39+
auto status = client.DeleteObject(bucket_name, object_name,
40+
gcs::Generation(object.generation()));
41+
if (!status.ok()) throw std::runtime_error(status.message());
42+
}
43+
44+
void DefaultClient(std::vector<std::string> const& argv) {
45+
namespace examples = ::google::cloud::storage::examples;
46+
if ((argv.size() == 1 && argv[0] == "--help") || argv.size() != 2) {
47+
throw examples::Usage{
48+
"default-client"
49+
" <bucket-name> <object-name>"};
50+
}
51+
//! [default-client]
52+
namespace gcs = google::cloud::storage;
53+
[](std::string const& bucket_name, std::string const& object_name) {
54+
auto client = gcs::Client::CreateDefaultClient();
55+
if (!client) throw std::runtime_error(client.status().message());
56+
57+
PerformSomeOperations(*client, bucket_name, object_name);
58+
}
59+
//! [default-client]
60+
(argv.at(0), argv.at(1));
61+
}
62+
63+
void ServiceAccountKeyfileJson(std::vector<std::string> const& argv) {
64+
namespace examples = ::google::cloud::storage::examples;
65+
if ((argv.size() == 1 && argv[0] == "--help") || argv.size() != 3) {
66+
throw examples::Usage{
67+
"service-account-keyfile-json"
68+
" <service-account-file> <bucket-name> <object-name>"};
69+
}
70+
//! [service-account-keyfile-json]
71+
namespace gcs = google::cloud::storage;
72+
[](std::string const& filename, std::string const& bucket_name,
73+
std::string const& object_name) {
74+
auto credentials =
75+
gcs::oauth2::CreateServiceAccountCredentialsFromFilePath(filename);
76+
if (!credentials) throw std::runtime_error(credentials.status().message());
77+
78+
PerformSomeOperations(gcs::Client(gcs::ClientOptions(*credentials)),
79+
bucket_name, object_name);
80+
}
81+
//! [service-account-keyfile-json]
82+
(argv.at(0), argv.at(1), argv.at(2));
83+
}
84+
85+
void ServiceAccountContentsJson(std::vector<std::string> const& argv) {
86+
namespace examples = ::google::cloud::storage::examples;
87+
if ((argv.size() == 1 && argv[0] == "--help") || argv.size() != 3) {
88+
throw examples::Usage{
89+
"service-account-contents-json"
90+
" <service-account-file> <bucket-name> <object-name>"};
91+
}
92+
//! [service-account-contents-json]
93+
namespace gcs = google::cloud::storage;
94+
[](std::string const& filename, std::string const& bucket_name,
95+
std::string const& object_name) {
96+
auto is = std::ifstream(filename);
97+
is.exceptions(std::ios::badbit);
98+
auto const contents =
99+
std::string{std::istreambuf_iterator<char>(is.rdbuf()), {}};
100+
auto credentials =
101+
gcs::oauth2::CreateServiceAccountCredentialsFromJsonContents(contents);
102+
if (!credentials) throw std::runtime_error(credentials.status().message());
103+
104+
PerformSomeOperations(gcs::Client(gcs::ClientOptions(*credentials)),
105+
bucket_name, object_name);
106+
}
107+
//! [service-account-contents-json]
108+
(argv.at(0), argv.at(1), argv.at(2));
109+
}
110+
111+
void RunAll(std::vector<std::string> const& argv) {
112+
namespace examples = ::google::cloud::storage::examples;
113+
namespace gcs = ::google::cloud::storage;
114+
115+
if (!argv.empty()) throw examples::Usage{"auto"};
116+
examples::CheckEnvironmentVariablesAreSet({
117+
"GOOGLE_CLOUD_PROJECT",
118+
});
119+
auto const project_id =
120+
google::cloud::internal::GetEnv("GOOGLE_CLOUD_PROJECT").value();
121+
auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
122+
auto const bucket_name = examples::MakeRandomBucketName(generator);
123+
auto client = gcs::Client::CreateDefaultClient().value();
124+
std::cout << "\nCreating bucket to run the example (" << bucket_name << ")"
125+
<< std::endl;
126+
(void)client
127+
.CreateBucketForProject(bucket_name, project_id, gcs::BucketMetadata{})
128+
.value();
129+
// In GCS a single project cannot create or delete buckets more often than
130+
// once every two seconds. We will pause until that time before deleting the
131+
// bucket.
132+
auto const delete_after =
133+
std::chrono::steady_clock::now() + std::chrono::seconds(2);
134+
135+
std::cout << "\nRunning DefaultClient()" << std::endl;
136+
auto const object_name = examples::MakeRandomObjectName(generator, "object-");
137+
DefaultClient({bucket_name, object_name});
138+
139+
auto const filename = google::cloud::internal::GetEnv(
140+
"GOOGLE_CLOUD_CPP_STORAGE_TEST_KEY_FILE_JSON");
141+
if (filename.has_value()) {
142+
std::cout << "\nRunning ServiceAccountContentsJson()" << std::endl;
143+
ServiceAccountContentsJson({*filename, bucket_name, object_name});
144+
145+
std::cout << "\nRunning ServiceAccountKeyfileJson()" << std::endl;
146+
ServiceAccountKeyfileJson({*filename, bucket_name, object_name});
147+
}
148+
149+
if (!examples::UsingEmulator()) std::this_thread::sleep_until(delete_after);
150+
(void)examples::RemoveBucketAndContents(client, bucket_name);
151+
}
152+
153+
} // namespace
154+
155+
int main(int argc, char* argv[]) {
156+
namespace examples = ::google::cloud::storage::examples;
157+
examples::Example example({
158+
{"default-client", DefaultClient},
159+
{"service-account-contents-json", ServiceAccountContentsJson},
160+
{"service-account-keyfile-json", ServiceAccountKeyfileJson},
161+
{"auto", RunAll},
162+
});
163+
return example.Run(argc, argv);
164+
}

google/cloud/storage/examples/storage_examples.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"""Automatically generated unit tests list - DO NOT EDIT."""
1818

1919
storage_examples = [
20+
"storage_auth_samples.cc",
2021
"storage_bucket_acl_samples.cc",
2122
"storage_bucket_cors_samples.cc",
2223
"storage_bucket_default_kms_key_samples.cc",

google/cloud/storage/oauth2/google_credentials.cc

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
#include "google/cloud/storage/oauth2/google_credentials.h"
1616
#include "google/cloud/storage/internal/make_jwt_assertion.h"
17-
#include "google/cloud/storage/internal/self_signing_service_account_credentials.h"
1817
#include "google/cloud/storage/oauth2/anonymous_credentials.h"
1918
#include "google/cloud/storage/oauth2/authorized_user_credentials.h"
2019
#include "google/cloud/storage/oauth2/compute_engine_credentials.h"
@@ -314,22 +313,6 @@ CreateServiceAccountCredentialsFromJsonContents(
314313
components.first, components.second, info->private_key);
315314
if (!jwt_assertion) return std::move(jwt_assertion).status();
316315

317-
if (!subject.has_value()) {
318-
auto constexpr kStorageScope = "https://storage.googleapis.com/";
319-
auto const matching_scope = [&] {
320-
if (!scopes.has_value()) return true;
321-
return scopes->size() == 1 && scopes->count(kStorageScope) == 1;
322-
}();
323-
if (matching_scope) {
324-
return StatusOr<std::shared_ptr<Credentials>>(
325-
std::make_shared<internal::SelfSigningServiceAccountCredentials>(
326-
internal::SelfSigningServiceAccountCredentialsInfo{
327-
/*.client_email*/ std::move(info->client_email),
328-
/*.private_key_id*/ std::move(info->private_key_id),
329-
/*.private_key*/ std::move(info->private_key),
330-
/*.audience*/ kStorageScope}));
331-
}
332-
}
333316
// These are supplied as extra parameters to this method, not in the JSON
334317
// file.
335318
info->subject = std::move(subject);

0 commit comments

Comments
 (0)