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