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