Google Cloud Storage C++ Client  1.42.0
A C++ Client Library for Google Cloud Storage
compute_engine_credentials.h
Go to the documentation of this file.
1 // Copyright 2018 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 // https://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 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H
17 
18 #include "google/cloud/storage/internal/compute_engine_util.h"
19 #include "google/cloud/storage/internal/curl_request_builder.h"
20 #include "google/cloud/storage/internal/openssl_util.h"
21 #include "google/cloud/storage/oauth2/credential_constants.h"
22 #include "google/cloud/storage/oauth2/credentials.h"
23 #include "google/cloud/storage/oauth2/refreshing_credentials_wrapper.h"
24 #include "google/cloud/storage/version.h"
25 #include "google/cloud/internal/getenv.h"
26 #include "google/cloud/status.h"
27 #include <chrono>
28 #include <ctime>
29 #include <mutex>
30 #include <set>
31 #include <string>
32 
33 namespace google {
34 namespace cloud {
35 namespace storage {
37 namespace oauth2 {
38 /// A helper struct that contains service account metadata.
40  std::set<std::string> scopes;
41  std::string email;
42 };
43 
44 /// Parses a metadata server response JSON string into a ServiceAccountMetadata.
46  storage::internal::HttpResponse const& response);
47 
48 /// Parses a refresh response JSON string into an authorization header. The
49 /// header and the current time (for the expiration) form a TemporaryToken.
52  storage::internal::HttpResponse const& response,
53  std::chrono::system_clock::time_point now);
54 
55 /**
56  * Wrapper class for Google OAuth 2.0 GCE instance service account credentials.
57  *
58  * Takes a service account email address or alias (e.g. "default") and uses the
59  * Google Compute Engine instance's metadata server to obtain service account
60  * metadata and OAuth 2.0 access tokens as needed. Instances of this class
61  * should usually be created via the convenience methods declared in
62  * google_credentials.h.
63  *
64  * An HTTP Authorization header, with an access token as its value, can be
65  * obtained by calling the AuthorizationHeader() method; if the current access
66  * token is invalid or nearing expiration, this will class will first obtain a
67  * new access token before returning the Authorization header string.
68  *
69  * @see https://cloud.google.com/compute/docs/authentication#using for details
70  * on how to get started with Compute Engine service account credentials.
71  *
72  * @tparam HttpRequestBuilderType a dependency injection point. It makes it
73  * possible to mock internal libcurl wrappers. This should generally not
74  * be overridden except for testing.
75  * @tparam ClockType a dependency injection point to fetch the current time.
76  * This should generally not be overridden except for testing.
77  */
78 template <typename HttpRequestBuilderType =
79  storage::internal::CurlRequestBuilder,
80  typename ClockType = std::chrono::system_clock>
82  public:
84 
85  explicit ComputeEngineCredentials(std::string service_account_email)
86  : clock_(), service_account_email_(std::move(service_account_email)) {}
87 
88  StatusOr<std::string> AuthorizationHeader() override {
89  std::unique_lock<std::mutex> lock(mu_);
90  return refreshing_creds_.AuthorizationHeader(clock_.now(),
91  [this] { return Refresh(); });
92  }
93 
94  std::string AccountEmail() const override {
95  std::unique_lock<std::mutex> lock(mu_);
96  // Force a refresh on the account info.
97  RetrieveServiceAccountInfo();
98  return service_account_email_;
99  }
100 
101  /**
102  * Returns the email or alias of this credential's service account.
103  *
104  * @note This class must query the Compute Engine instance's metadata server
105  * to fetch service account metadata. Because of this, if an alias (e.g.
106  * "default") was supplied in place of an actual email address when
107  * initializing this credential, that alias is returned as this credential's
108  * email address if the credential has not been refreshed yet.
109  */
110  std::string service_account_email() const {
111  std::unique_lock<std::mutex> lock(mu_);
112  return service_account_email_;
113  }
114 
115  /**
116  * Returns the set of scopes granted to this credential's service account.
117  *
118  * @note Because this class must query the Compute Engine instance's metadata
119  * server to fetch service account metadata, this method will return an empty
120  * set if the credential has not been refreshed yet.
121  */
122  std::set<std::string> scopes() const {
123  std::unique_lock<std::mutex> lock(mu_);
124  return scopes_;
125  }
126 
127  private:
128  /**
129  * Sends an HTTP GET request to the GCE metadata server.
130  *
131  * @see https://cloud.google.com/compute/docs/storing-retrieving-metadata for
132  * an overview of retrieving information from the GCE metadata server.
133  */
134  StatusOr<storage::internal::HttpResponse> DoMetadataServerGetRequest(
135  std::string const& path, bool recursive) const {
136  // Allows mocking the metadata server hostname for testing.
137  std::string metadata_server_hostname =
138  google::cloud::storage::internal::GceMetadataHostname();
139 
140  HttpRequestBuilderType builder(
141  std::move("http://" + metadata_server_hostname + path),
142  storage::internal::GetDefaultCurlHandleFactory());
143  builder.AddHeader("metadata-flavor: Google");
144  if (recursive) builder.AddQueryParameter("recursive", "true");
145  return std::move(builder).BuildRequest().MakeRequest(std::string{});
146  }
147 
148  /**
149  * Fetches metadata for an instance's service account.
150  *
151  * @see
152  * https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances
153  * for more details.
154  */
155  Status RetrieveServiceAccountInfo() const {
156  auto response = DoMetadataServerGetRequest(
157  "/computeMetadata/v1/instance/service-accounts/" +
158  service_account_email_ + "/",
159  true);
160  if (!response) {
161  return std::move(response).status();
162  }
163  if (response->status_code >= 300) {
164  return AsStatus(*response);
165  }
166 
167  auto metadata = ParseMetadataServerResponse(*response);
168  if (!metadata) {
169  return metadata.status();
170  }
171  service_account_email_ = std::move(metadata->email);
172  scopes_ = std::move(metadata->scopes);
173  return Status();
174  }
175 
176  StatusOr<RefreshingCredentialsWrapper::TemporaryToken> Refresh() const {
177  auto status = RetrieveServiceAccountInfo();
178  if (!status.ok()) {
179  return status;
180  }
181 
182  auto response = DoMetadataServerGetRequest(
183  "/computeMetadata/v1/instance/service-accounts/" +
184  service_account_email_ + "/token",
185  false);
186  if (!response) {
187  return std::move(response).status();
188  }
189  if (response->status_code >= 300) {
190  return AsStatus(*response);
191  }
192 
193  return ParseComputeEngineRefreshResponse(*response, clock_.now());
194  }
195 
196  ClockType clock_;
197  mutable std::mutex mu_;
198  RefreshingCredentialsWrapper refreshing_creds_;
199  mutable std::set<std::string> scopes_;
200  mutable std::string service_account_email_;
201 };
202 
203 } // namespace oauth2
205 } // namespace storage
206 } // namespace cloud
207 } // namespace google
208 
209 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_STORAGE_OAUTH2_COMPUTE_ENGINE_CREDENTIALS_H