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 <grpc/support/port_platform.h>
17
18 #include "src/core/lib/security/credentials/external/file_external_account_credentials.h"
19
20 #include <map>
21 #include <utility>
22
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/string_view.h"
26
27 #include <grpc/slice.h>
28 #include <grpc/support/json.h>
29
30 #include "src/core/lib/iomgr/load_file.h"
31 #include "src/core/lib/json/json.h"
32 #include "src/core/lib/json/json_reader.h"
33 #include "src/core/lib/slice/slice.h"
34 #include "src/core/lib/slice/slice_internal.h"
35
36 namespace grpc_core {
37
38 RefCountedPtr<FileExternalAccountCredentials>
Create(Options options,std::vector<std::string> scopes,grpc_error_handle * error)39 FileExternalAccountCredentials::Create(Options options,
40 std::vector<std::string> scopes,
41 grpc_error_handle* error) {
42 auto creds = MakeRefCounted<FileExternalAccountCredentials>(
43 std::move(options), std::move(scopes), error);
44 if (error->ok()) {
45 return creds;
46 } else {
47 return nullptr;
48 }
49 }
50
FileExternalAccountCredentials(Options options,std::vector<std::string> scopes,grpc_error_handle * error)51 FileExternalAccountCredentials::FileExternalAccountCredentials(
52 Options options, std::vector<std::string> scopes, grpc_error_handle* error)
53 : ExternalAccountCredentials(options, std::move(scopes)) {
54 auto it = options.credential_source.object().find("file");
55 if (it == options.credential_source.object().end()) {
56 *error = GRPC_ERROR_CREATE("file field not present.");
57 return;
58 }
59 if (it->second.type() != Json::Type::kString) {
60 *error = GRPC_ERROR_CREATE("file field must be a string.");
61 return;
62 }
63 file_ = it->second.string();
64 it = options.credential_source.object().find("format");
65 if (it != options.credential_source.object().end()) {
66 const Json& format_json = it->second;
67 if (format_json.type() != Json::Type::kObject) {
68 *error = GRPC_ERROR_CREATE(
69 "The JSON value of credential source format is not an object.");
70 return;
71 }
72 auto format_it = format_json.object().find("type");
73 if (format_it == format_json.object().end()) {
74 *error = GRPC_ERROR_CREATE("format.type field not present.");
75 return;
76 }
77 if (format_it->second.type() != Json::Type::kString) {
78 *error = GRPC_ERROR_CREATE("format.type field must be a string.");
79 return;
80 }
81 format_type_ = format_it->second.string();
82 if (format_type_ == "json") {
83 format_it = format_json.object().find("subject_token_field_name");
84 if (format_it == format_json.object().end()) {
85 *error = GRPC_ERROR_CREATE(
86 "format.subject_token_field_name field must be present if the "
87 "format is in Json.");
88 return;
89 }
90 if (format_it->second.type() != Json::Type::kString) {
91 *error = GRPC_ERROR_CREATE(
92 "format.subject_token_field_name field must be a string.");
93 return;
94 }
95 format_subject_token_field_name_ = format_it->second.string();
96 }
97 }
98 }
99
RetrieveSubjectToken(HTTPRequestContext *,const Options &,std::function<void (std::string,grpc_error_handle)> cb)100 void FileExternalAccountCredentials::RetrieveSubjectToken(
101 HTTPRequestContext* /*ctx*/, const Options& /*options*/,
102 std::function<void(std::string, grpc_error_handle)> cb) {
103 struct SliceWrapper {
104 ~SliceWrapper() { CSliceUnref(slice); }
105 grpc_slice slice = grpc_empty_slice();
106 };
107 SliceWrapper content_slice;
108 // To retrieve the subject token, we read the file every time we make a
109 // request because it may have changed since the last request.
110 grpc_error_handle error =
111 grpc_load_file(file_.c_str(), 0, &content_slice.slice);
112 if (!error.ok()) {
113 cb("", error);
114 return;
115 }
116 absl::string_view content = StringViewFromSlice(content_slice.slice);
117 if (format_type_ == "json") {
118 auto content_json = JsonParse(content);
119 if (!content_json.ok() || content_json->type() != Json::Type::kObject) {
120 cb("", GRPC_ERROR_CREATE(
121 "The content of the file is not a valid json object."));
122 return;
123 }
124 auto content_it =
125 content_json->object().find(format_subject_token_field_name_);
126 if (content_it == content_json->object().end()) {
127 cb("", GRPC_ERROR_CREATE("Subject token field not present."));
128 return;
129 }
130 if (content_it->second.type() != Json::Type::kString) {
131 cb("", GRPC_ERROR_CREATE("Subject token field must be a string."));
132 return;
133 }
134 cb(content_it->second.string(), absl::OkStatus());
135 return;
136 }
137 cb(std::string(content), absl::OkStatus());
138 }
139
140 } // namespace grpc_core
141