common/cpp/obj-client/auth/gcp/gcp_token_provider.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "cpp/obj-client/auth/gcp/gcp_token_provider.h" |
19 | | |
20 | | #include <glog/logging.h> |
21 | | #include <google/cloud/credentials.h> |
22 | | #include <google/cloud/internal/oauth2_decorate_credentials.h> |
23 | | #include <google/cloud/internal/oauth2_error_credentials.h> |
24 | | #include <google/cloud/internal/oauth2_google_application_default_credentials_file.h> |
25 | | #include <google/cloud/internal/oauth2_service_account_credentials.h> |
26 | | #include <google/cloud/internal/rest_client.h> |
27 | | #include <google/cloud/internal/unified_rest_credentials.h> |
28 | | #include <rapidjson/document.h> |
29 | | |
30 | | #include <fstream> |
31 | | #include <iterator> |
32 | | #include <set> |
33 | | #include <string_view> |
34 | | #include <utility> |
35 | | |
36 | | namespace doris { |
37 | | namespace { |
38 | | |
39 | | constexpr char GCS_READ_WRITE_SCOPE[] = "https://www.googleapis.com/auth/devstorage.read_write"; |
40 | | constexpr char CLOUD_PLATFORM_SCOPE[] = "https://www.googleapis.com/auth/cloud-platform"; |
41 | | |
42 | | std::shared_ptr<google::cloud::oauth2_internal::Credentials> make_default_storage_credentials( |
43 | 16 | const google::cloud::Options& options) { |
44 | 16 | namespace oauth2 = google::cloud::oauth2_internal; |
45 | 16 | auto path = oauth2::GoogleAdcFilePathFromEnvVarOrEmpty(); |
46 | 16 | if (path.empty()) { |
47 | 13 | path = oauth2::GoogleAdcFilePathFromWellKnownPathOrEmpty(); |
48 | 13 | } |
49 | 16 | std::ifstream input(path); |
50 | 16 | std::string contents(std::istreambuf_iterator<char> {input}, {}); |
51 | 16 | rapidjson::Document document; |
52 | 16 | document.Parse(contents.data(), contents.size()); |
53 | | // The generic SDK adapter emits scoped self-signed JWTs for service-account |
54 | | // JSON keys, which GCS rejects. Keep other ADC types (and their errors) on |
55 | | // the SDK's normal resolution path, including federation and VM identities. |
56 | 16 | if (document.HasParseError() || !document.IsObject() || !document.HasMember("type") || |
57 | 16 | !document["type"].IsString() || |
58 | 16 | std::string_view(document["type"].GetString(), document["type"].GetStringLength()) != |
59 | 12 | "service_account") { |
60 | 12 | return google::cloud::rest_internal::MapCredentials( |
61 | 12 | *google::cloud::MakeGoogleDefaultCredentials(options)); |
62 | 12 | } |
63 | 4 | auto info = oauth2::ParseServiceAccountCredentials(contents, path); |
64 | 4 | if (!info) { |
65 | 1 | return std::make_shared<oauth2::ErrorCredentials>(info.status()); |
66 | 1 | } |
67 | 3 | info->enable_self_signed_jwt = false; |
68 | 3 | const auto& scopes = options.get<google::cloud::ScopesOption>(); |
69 | 3 | info->scopes = std::set<std::string>(scopes.begin(), scopes.end()); |
70 | 3 | return oauth2::Decorate(std::make_shared<oauth2::ServiceAccountCredentials>( |
71 | 3 | *info, options, |
72 | 3 | [](const google::cloud::Options& opts) { |
73 | 3 | return google::cloud::rest_internal::MakeDefaultRestClient( |
74 | 3 | "", opts); |
75 | 3 | }), |
76 | 3 | options); |
77 | 4 | } |
78 | | |
79 | | std::shared_ptr<google::cloud::oauth2_internal::Credentials> make_credentials( |
80 | | const GcpCredentialConfig& credential, const std::string& ca_cert_path, |
81 | 16 | GcpTokenScope token_scope) { |
82 | 16 | google::cloud::Options options; |
83 | 16 | if (!ca_cert_path.empty()) { |
84 | 0 | options.set<google::cloud::CARootsFilePathOption>(ca_cert_path); |
85 | 0 | } |
86 | | |
87 | | // A source credential used for impersonation calls the IAM Credentials API |
88 | | // and therefore needs cloud-platform. The resulting target token uses the |
89 | | // caller-selected scope: object read/write for storage requests, or |
90 | | // cloud-platform when it will call IAM signBlob. |
91 | 16 | const char* requested_scope = token_scope == GcpTokenScope::CloudPlatform |
92 | 16 | ? CLOUD_PLATFORM_SCOPE |
93 | 16 | : GCS_READ_WRITE_SCOPE; |
94 | 16 | options.set<google::cloud::ScopesOption>({credential.impersonation_service_account.empty() |
95 | 16 | ? requested_scope |
96 | 16 | : CLOUD_PLATFORM_SCOPE}); |
97 | | |
98 | 16 | std::shared_ptr<google::cloud::Credentials> credentials; |
99 | 16 | switch (credential.provider_type) { |
100 | 16 | case GcpCredentialProviderType::Default: |
101 | | // A direct service-account ADC token goes to GCS. Impersonation sources |
102 | | // instead authenticate to IAM, which accepts the SDK's self-signed JWTs. |
103 | 16 | if (credential.impersonation_service_account.empty()) { |
104 | 16 | return make_default_storage_credentials(options); |
105 | 16 | } |
106 | 0 | credentials = google::cloud::MakeGoogleDefaultCredentials(options); |
107 | 0 | break; |
108 | 0 | case GcpCredentialProviderType::ComputeEngine: |
109 | 0 | credentials = google::cloud::MakeComputeEngineCredentials(options); |
110 | 0 | break; |
111 | 16 | } |
112 | 0 | if (!credential.impersonation_service_account.empty()) { |
113 | 0 | auto impersonation_options = options; |
114 | 0 | impersonation_options.set<google::cloud::ScopesOption>({requested_scope}); |
115 | 0 | credentials = google::cloud::MakeImpersonateServiceAccountCredentials( |
116 | 0 | std::move(credentials), credential.impersonation_service_account, |
117 | 0 | std::move(impersonation_options)); |
118 | 0 | } |
119 | 0 | return google::cloud::rest_internal::MapCredentials(*credentials); |
120 | 16 | } |
121 | | |
122 | | } // namespace |
123 | | |
124 | | GcpTokenProvider::GcpTokenProvider(const GcpCredentialConfig& credential, |
125 | | const std::string& ca_cert_path, GcpTokenScope token_scope) |
126 | 16 | : GcpTokenProvider(make_credentials(credential, ca_cert_path, token_scope)) {} |
127 | | |
128 | | GcpTokenProvider::GcpTokenProvider( |
129 | | std::shared_ptr<google::cloud::oauth2_internal::Credentials> credentials) |
130 | 24 | : _credentials(std::move(credentials)) {} |
131 | | |
132 | 24 | GcpTokenProvider::~GcpTokenProvider() = default; |
133 | | |
134 | 11 | std::optional<std::string> GcpTokenProvider::get_token() const { |
135 | | // GcpS3Client and presign callers may share one provider across request |
136 | | // threads. Serialize access even if the underlying implementation changes |
137 | | // its thread-safety guarantees in a future google-cloud-cpp release. |
138 | 11 | std::lock_guard lock(_credentials_mutex); |
139 | 11 | auto token = _credentials->GetToken(std::chrono::system_clock::now()); |
140 | 11 | if (!token) { |
141 | 2 | LOG_EVERY_N(WARNING, 100) << "failed to obtain GCP access token: " |
142 | 1 | << token.status().message(); |
143 | 2 | return std::nullopt; |
144 | 2 | } |
145 | 9 | return token->token; |
146 | 11 | } |
147 | | |
148 | 8 | std::string GcpTokenProvider::get_service_account_email() const { |
149 | 8 | std::lock_guard lock(_credentials_mutex); |
150 | 8 | return _credentials->AccountEmail(); |
151 | 8 | } |
152 | | |
153 | | } // namespace doris |