• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2020 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include "src/core/lib/security/credentials/external/file_external_account_credentials.h"
17 
18 #include <grpc/slice.h>
19 #include <grpc/support/json.h>
20 #include <grpc/support/port_platform.h>
21 
22 #include <map>
23 #include <utility>
24 
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/string_view.h"
28 #include "src/core/lib/slice/slice.h"
29 #include "src/core/lib/slice/slice_internal.h"
30 #include "src/core/util/json/json.h"
31 #include "src/core/util/json/json_reader.h"
32 #include "src/core/util/load_file.h"
33 
34 namespace grpc_core {
35 
36 //
37 // FileExternalAccountCredentials::FileFetchBody
38 //
39 
FileFetchBody(absl::AnyInvocable<void (absl::StatusOr<std::string>)> on_done,FileExternalAccountCredentials * creds)40 FileExternalAccountCredentials::FileFetchBody::FileFetchBody(
41     absl::AnyInvocable<void(absl::StatusOr<std::string>)> on_done,
42     FileExternalAccountCredentials* creds)
43     : FetchBody(std::move(on_done)), creds_(creds) {
44   // Start work asynchronously, since we can't invoke the callback
45   // synchronously without causing a deadlock.
46   creds->event_engine().Run([self = RefAsSubclass<FileFetchBody>()]() mutable {
47     ApplicationCallbackExecCtx application_exec_ctx;
48     ExecCtx exec_ctx;
49     self->ReadFile();
50     self.reset();
51   });
52 }
53 
ReadFile()54 void FileExternalAccountCredentials::FileFetchBody::ReadFile() {
55   // To retrieve the subject token, we read the file every time we make a
56   // request because it may have changed since the last request.
57   auto content_slice = LoadFile(creds_->file_, /*add_null_terminator=*/false);
58   if (!content_slice.ok()) {
59     Finish(content_slice.status());
60     return;
61   }
62   absl::string_view content = content_slice->as_string_view();
63   if (creds_->format_type_ == "json") {
64     auto content_json = JsonParse(content);
65     if (!content_json.ok() || content_json->type() != Json::Type::kObject) {
66       Finish(GRPC_ERROR_CREATE(
67           "The content of the file is not a valid json object."));
68       return;
69     }
70     auto content_it =
71         content_json->object().find(creds_->format_subject_token_field_name_);
72     if (content_it == content_json->object().end()) {
73       Finish(GRPC_ERROR_CREATE("Subject token field not present."));
74       return;
75     }
76     if (content_it->second.type() != Json::Type::kString) {
77       Finish(GRPC_ERROR_CREATE("Subject token field must be a string."));
78       return;
79     }
80     Finish(content_it->second.string());
81     return;
82   }
83   Finish(std::string(content));
84 }
85 
86 //
87 // FileExternalAccountCredentials
88 //
89 
90 absl::StatusOr<RefCountedPtr<FileExternalAccountCredentials>>
Create(Options options,std::vector<std::string> scopes,std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine)91 FileExternalAccountCredentials::Create(
92     Options options, std::vector<std::string> scopes,
93     std::shared_ptr<grpc_event_engine::experimental::EventEngine>
94         event_engine) {
95   grpc_error_handle error;
96   auto creds = MakeRefCounted<FileExternalAccountCredentials>(
97       std::move(options), std::move(scopes), std::move(event_engine), &error);
98   if (!error.ok()) return error;
99   return creds;
100 }
101 
FileExternalAccountCredentials(Options options,std::vector<std::string> scopes,std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine,grpc_error_handle * error)102 FileExternalAccountCredentials::FileExternalAccountCredentials(
103     Options options, std::vector<std::string> scopes,
104     std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine,
105     grpc_error_handle* error)
106     : ExternalAccountCredentials(options, std::move(scopes),
107                                  std::move(event_engine)) {
108   auto it = options.credential_source.object().find("file");
109   if (it == options.credential_source.object().end()) {
110     *error = GRPC_ERROR_CREATE("file field not present.");
111     return;
112   }
113   if (it->second.type() != Json::Type::kString) {
114     *error = GRPC_ERROR_CREATE("file field must be a string.");
115     return;
116   }
117   file_ = it->second.string();
118   it = options.credential_source.object().find("format");
119   if (it != options.credential_source.object().end()) {
120     const Json& format_json = it->second;
121     if (format_json.type() != Json::Type::kObject) {
122       *error = GRPC_ERROR_CREATE(
123           "The JSON value of credential source format is not an object.");
124       return;
125     }
126     auto format_it = format_json.object().find("type");
127     if (format_it == format_json.object().end()) {
128       *error = GRPC_ERROR_CREATE("format.type field not present.");
129       return;
130     }
131     if (format_it->second.type() != Json::Type::kString) {
132       *error = GRPC_ERROR_CREATE("format.type field must be a string.");
133       return;
134     }
135     format_type_ = format_it->second.string();
136     if (format_type_ == "json") {
137       format_it = format_json.object().find("subject_token_field_name");
138       if (format_it == format_json.object().end()) {
139         *error = GRPC_ERROR_CREATE(
140             "format.subject_token_field_name field must be present if the "
141             "format is in Json.");
142         return;
143       }
144       if (format_it->second.type() != Json::Type::kString) {
145         *error = GRPC_ERROR_CREATE(
146             "format.subject_token_field_name field must be a string.");
147         return;
148       }
149       format_subject_token_field_name_ = format_it->second.string();
150     }
151   }
152 }
153 
debug_string()154 std::string FileExternalAccountCredentials::debug_string() {
155   return absl::StrCat("FileExternalAccountCredentials{Audience:", audience(),
156                       ")");
157 }
158 
Type()159 UniqueTypeName FileExternalAccountCredentials::Type() {
160   static UniqueTypeName::Factory kFactory("FileExternalAccountCredentials");
161   return kFactory.Create();
162 }
163 
164 OrphanablePtr<ExternalAccountCredentials::FetchBody>
RetrieveSubjectToken(Timestamp,absl::AnyInvocable<void (absl::StatusOr<std::string>)> on_done)165 FileExternalAccountCredentials::RetrieveSubjectToken(
166     Timestamp /*deadline*/,
167     absl::AnyInvocable<void(absl::StatusOr<std::string>)> on_done) {
168   return MakeOrphanable<FileFetchBody>(std::move(on_done), this);
169 }
170 
CredentialSourceType()171 absl::string_view FileExternalAccountCredentials::CredentialSourceType() {
172   return "file";
173 }
174 
175 }  // namespace grpc_core
176