Google Cloud Storage C++ Client  1.32.1
A C++ Client Library for Google Cloud Storage
iam_policy.cc
Go to the documentation of this file.
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "google/cloud/storage/iam_policy.h"
16 #include "absl/types/optional.h"
17 #include <nlohmann/json.hpp>
18 #include <sstream>
19 
20 namespace google {
21 namespace cloud {
22 namespace storage {
23 inline namespace STORAGE_CLIENT_NS {
24 namespace {
25 template <typename Functor>
26 Status IsOfTypeIfPresent(nlohmann::json const& json,
27  std::string const& json_rep, std::string const& field,
28  std::string const& location_desc, Functor const& check,
29  std::string const& type_desc) {
30  if (!field.empty() && json.find(field) == json.end()) {
31  return Status();
32  }
33  nlohmann::json const& to_check = field.empty() ? json : json[field];
34  if (!check(to_check)) {
35  std::ostringstream os;
36  os << "Invalid IamPolicy payload, expected " << type_desc << " for "
37  << location_desc << ". payload=" << json_rep;
39  }
40  return Status();
41 }
42 
43 Status IsStringIfPresent(nlohmann::json const& json,
44  std::string const& json_rep, std::string const& field,
45  std::string const& location_desc) {
46  return IsOfTypeIfPresent(
47  json, json_rep, field, location_desc,
48  [](nlohmann::json const& json) { return json.is_string(); }, "string");
49 }
50 
51 Status IsIntIfPresent(nlohmann::json const& json, std::string const& json_rep,
52  std::string const& field,
53  std::string const& location_desc) {
54  return IsOfTypeIfPresent(
55  json, json_rep, field, location_desc,
56  [](nlohmann::json const& json) { return json.is_number_integer(); },
57  "integer");
58 }
59 
60 Status IsObjectIfPresent(nlohmann::json const& json,
61  std::string const& json_rep, std::string const& field,
62  std::string const& location_desc) {
63  return IsOfTypeIfPresent(
64  json, json_rep, field, location_desc,
65  [](nlohmann::json const& json) { return json.is_object(); }, "object");
66 }
67 
68 Status IsArrayIfPresent(nlohmann::json const& json, std::string const& json_rep,
69  std::string const& field,
70  std::string const& location_desc) {
71  return IsOfTypeIfPresent(
72  json, json_rep, field, location_desc,
73  [](nlohmann::json const& json) { return json.is_array(); }, "array");
74 }
75 
76 } // namespace
77 
79  static StatusOr<NativeExpression> CreateFromJson(
80  nlohmann::json const& json, std::string const& policy_json_rep) {
81  Status status = IsStringIfPresent(json, policy_json_rep, "expression",
82  "'expression' field");
83  if (!status.ok()) {
84  return status;
85  }
86  status = IsStringIfPresent(json, policy_json_rep, "title", "'title' field");
87  if (!status.ok()) {
88  return status;
89  }
90  status = IsStringIfPresent(json, policy_json_rep, "description",
91  "'description' field");
92  if (!status.ok()) {
93  return status;
94  }
95  status = IsStringIfPresent(json, policy_json_rep, "location",
96  "'location' field");
97  if (!status.ok()) {
98  return status;
99  }
100  // Cannot use make_unique because we are using brace-initialization.
101  // NOLINTNEXTLINE(modernize-make-unique)
102  return NativeExpression(std::unique_ptr<Impl>(new Impl{json}));
103  }
104 
105  nlohmann::json ToJson() const { return native_json; }
106 
107  nlohmann::json native_json;
108 };
109 
110 NativeExpression::NativeExpression(std::string expression, std::string title,
111  std::string description,
112  std::string location)
113  : pimpl_(new Impl{nlohmann::json{{"expression", std::move(expression)}}}) {
114  if (!title.empty()) {
115  pimpl_->native_json["title"] = std::move(title);
116  }
117  if (!description.empty()) {
118  pimpl_->native_json["description"] = std::move(description);
119  }
120  if (!location.empty()) {
121  pimpl_->native_json["location"] = std::move(location);
122  }
123 }
124 
126  : pimpl_(new Impl(*other.pimpl_)) {}
127 
128 NativeExpression::~NativeExpression() = default;
129 
130 NativeExpression::NativeExpression(std::unique_ptr<Impl> impl)
131  : pimpl_(std::move(impl)) {}
132 
134  *pimpl_ = *other.pimpl_;
135  return *this;
136 }
137 
139  : pimpl_(std::move(rhs.pimpl_)) {}
140 
142  pimpl_ = std::move(rhs.pimpl_);
143  return *this;
144 }
145 
146 std::string NativeExpression::expression() const {
147  return pimpl_->native_json.value("expression", "");
148 }
149 
150 void NativeExpression::set_expression(std::string expression) {
151  pimpl_->native_json["expression"] = std::move(expression);
152 }
153 
154 std::string NativeExpression::title() const {
155  return pimpl_->native_json.value("title", "");
156 }
157 
158 void NativeExpression::set_title(std::string title) {
159  pimpl_->native_json["title"] = std::move(title);
160 }
161 
162 std::string NativeExpression::description() const {
163  return pimpl_->native_json.value("description", "");
164 }
165 
166 void NativeExpression::set_description(std::string description) {
167  pimpl_->native_json["description"] = std::move(description);
168 }
169 
170 std::string NativeExpression::location() const {
171  return pimpl_->native_json.value("location", "");
172 }
173 
174 void NativeExpression::set_location(std::string location) {
175  pimpl_->native_json["location"] = std::move(location);
176 }
177 
178 std::ostream& operator<<(std::ostream& stream, NativeExpression const& e) {
179  stream << "(" << e.expression();
180  if (!e.title().empty()) {
181  stream << ", title=\"" << e.title() << "\"";
182  }
183  if (!e.description().empty()) {
184  stream << ", description=\"" << e.description() << "\"";
185  }
186  if (!e.location().empty()) {
187  stream << ", location=\"" << e.location() << "\"";
188  }
189  stream << ")";
190  return stream;
191 }
192 
195  nlohmann::json json, std::string const& policy_json_rep) {
196  Status status =
197  IsObjectIfPresent(json, policy_json_rep, "", "'bindings' entry");
198  if (!status.ok()) {
199  return status;
200  }
201  status = IsStringIfPresent(json, policy_json_rep, "role", "'role' field");
202  if (!status.ok()) {
203  return status;
204  }
205  std::string role = json.value("role", "");
206  status =
207  IsArrayIfPresent(json, policy_json_rep, "members", "'members' field");
208  if (!status.ok()) {
209  return status;
210  }
211  std::vector<std::string> members;
212  auto members_it = json.find("members");
213  if (members_it != json.end()) {
214  for (auto const& member : *members_it) {
215  status =
216  IsStringIfPresent(member, policy_json_rep, "", "'members' entry");
217  if (!status.ok()) {
218  return status;
219  }
220  members.emplace_back(member.get<std::string>());
221  }
222  json.erase(members_it);
223  }
224  status = IsObjectIfPresent(json, policy_json_rep, "condition",
225  "'condition' field");
226  if (!status.ok()) {
227  return status;
228  }
229  absl::optional<NativeExpression> condition;
230  auto condition_it = json.find("condition");
231  if (condition_it != json.end()) {
232  auto parsed_condition = NativeExpression::Impl::CreateFromJson(
233  *condition_it, policy_json_rep);
234  if (!parsed_condition) {
235  return parsed_condition.status();
236  }
237  condition = *std::move(parsed_condition);
238  json.erase(condition_it);
239  }
240  // Cannot use make_unique because we are using brace-initialization.
241  // NOLINTNEXTLINE(modernize-make-unique)
242  return NativeIamBinding(std::unique_ptr<Impl>(
243  new Impl{json, std::move(members), std::move(condition)}));
244  }
245 
246  nlohmann::json ToJson() const {
247  auto ret = native_json;
248  if (condition.has_value()) {
249  ret["condition"] = condition->pimpl_->ToJson();
250  }
251  if (!members.empty()) {
252  ret["members"] = members;
253  }
254  return ret;
255  }
256 
257  nlohmann::json native_json;
258  std::vector<std::string> members;
259  absl::optional<NativeExpression> condition;
260 };
261 
263  std::vector<std::string> members)
264  : pimpl_(new Impl{nlohmann::json{{"role", std::move(role)}},
265  std::move(members), absl::optional<NativeExpression>()}) {
266 }
267 
269  std::vector<std::string> members,
270  NativeExpression condition)
271  : pimpl_(new Impl{nlohmann::json{{"role", std::move(role)}},
272  std::move(members), std::move(condition)}) {}
273 
275  : pimpl_(new Impl(*other.pimpl_)) {}
276 
277 NativeIamBinding::NativeIamBinding(std::unique_ptr<Impl> impl)
278  : pimpl_(std::move(impl)) {}
279 
280 NativeIamBinding::~NativeIamBinding() = default;
281 
283  *pimpl_ = *other.pimpl_;
284  return *this;
285 }
286 
288  : pimpl_(std::move(rhs.pimpl_)) {}
289 
291  pimpl_ = std::move(rhs.pimpl_);
292  return *this;
293 }
294 
295 std::string NativeIamBinding::role() const {
296  return pimpl_->native_json.value("role", "");
297 }
298 
299 void NativeIamBinding::set_role(std::string role) {
300  pimpl_->native_json["role"] = std::move(role);
301 }
302 
303 std::vector<std::string> const& NativeIamBinding::members() const {
304  return pimpl_->members;
305 }
306 
307 std::vector<std::string>& NativeIamBinding::members() {
308  return pimpl_->members;
309 }
310 
312  return *pimpl_->condition;
313 }
314 
316 
318  pimpl_->condition = std::move(condition);
319 }
320 
322  return pimpl_->condition.has_value();
323 }
324 
325 void NativeIamBinding::clear_condition() { pimpl_->condition.reset(); }
326 
327 std::ostream& operator<<(std::ostream& os, NativeIamBinding const& binding) {
328  os << binding.role() << ": [";
329  bool first = true;
330  for (auto const& member : binding.members()) {
331  os << (first ? "" : ", ") << member;
332  first = false;
333  }
334  os << "]";
335  if (binding.has_condition()) {
336  os << " when " << binding.condition();
337  }
338  return os;
339 }
340 
342  nlohmann::json ToJson() const {
343  auto ret = native_json;
344  if (!bindings.empty()) {
345  auto& ret_bindings = ret["bindings"] = nlohmann::json::array();
346  std::transform(bindings.begin(), bindings.end(),
347  std::back_inserter(ret_bindings),
348  [](NativeIamBinding const& binding) {
349  return binding.pimpl_->ToJson();
350  });
351  }
352  ret["kind"] = "storage#policy";
353  return ret;
354  }
355 
356  nlohmann::json native_json;
358 };
359 
361  std::string etag, std::int32_t version)
362  : pimpl_(
363  new Impl{nlohmann::json{{"version", version}}, std::move(bindings)}) {
364  if (!etag.empty()) {
365  pimpl_->native_json["etag"] = std::move(etag);
366  }
367 }
368 
370  : pimpl_(new Impl(*other.pimpl_)) {}
371 
372 NativeIamPolicy::NativeIamPolicy(std::unique_ptr<Impl> impl)
373  : pimpl_(std::move(impl)) {}
374 
375 NativeIamPolicy::~NativeIamPolicy() = default;
376 
378  std::string const& json_rep) {
379  auto json = nlohmann::json::parse(json_rep, nullptr, false);
380  // Make sure the json actually parsed successfully
381  if (json.is_discarded()) {
382  std::ostringstream os;
383  os << "Invalid IamPolicy payload, it failed to parse as valid JSON. "
384  "payload="
385  << json_rep;
387  }
388  Status status;
389  status = IsObjectIfPresent(json, json_rep, "", "top level node");
390  if (!status.ok()) {
391  return status;
392  }
393  status = IsIntIfPresent(json, json_rep, "version", "'version' field");
394  if (!status.ok()) {
395  return status;
396  }
397  status = IsStringIfPresent(json, json_rep, "etag", "'etag' field");
398  if (!status.ok()) {
399  return status;
400  }
401  status = IsArrayIfPresent(json, json_rep, "bindings", "'bindings' field");
402  if (!status.ok()) {
403  return status;
404  }
405  std::vector<NativeIamBinding> bindings;
406  auto binding_it = json.find("bindings");
407  if (binding_it != json.end()) {
408  for (auto const& kv : binding_it->items()) {
409  auto binding_impl =
411  if (!binding_impl) {
412  return binding_impl.status();
413  }
414  bindings.emplace_back(*std::move(binding_impl));
415  }
416  json.erase(binding_it);
417  }
418  // Cannot use make_unique because we are using brace-initialization.
419  // NOLINTNEXTLINE(modernize-make-unique)
420  return NativeIamPolicy(std::unique_ptr<NativeIamPolicy::Impl>(
421  new NativeIamPolicy::Impl{std::move(json), std::move(bindings)}));
422 }
423 
424 std::string NativeIamPolicy::ToJson() const { return pimpl_->ToJson().dump(); }
425 
427  *pimpl_ = *other.pimpl_;
428  return *this;
429 }
430 
431 // NOLINTNEXTLINE(readability-identifier-naming)
432 std::int32_t NativeIamPolicy::version() const {
433  return pimpl_->native_json.value("version", 0);
434 }
435 
436 // NOLINTNEXTLINE(readability-identifier-naming)
437 void NativeIamPolicy::set_version(std::int32_t version) {
438  pimpl_->native_json["version"] = version;
439 }
440 
441 // NOLINTNEXTLINE(readability-identifier-naming)
442 std::string NativeIamPolicy::etag() const {
443  return pimpl_->native_json.value("etag", "");
444 }
445 
446 // NOLINTNEXTLINE(readability-identifier-naming)
447 void NativeIamPolicy::set_etag(std::string etag) {
448  pimpl_->native_json["etag"] = std::move(etag);
449 }
450 
451 // NOLINTNEXTLINE(readability-identifier-naming)
453  return pimpl_->bindings;
454 }
455 
456 // NOLINTNEXTLINE(readability-identifier-naming)
457 std::vector<NativeIamBinding> const& NativeIamPolicy::bindings() const {
458  return pimpl_->bindings;
459 }
460 
461 std::ostream& operator<<(std::ostream& os, NativeIamPolicy const& rhs) {
462  os << "NativeIamPolicy={version=" << rhs.version() << ", bindings="
463  << "NativeIamBindings={";
464  bool first = true;
465  for (auto const& binding : rhs.bindings()) {
466  os << (first ? "" : ", ") << binding;
467  first = false;
468  }
469  return os << "}, etag=" << rhs.etag() << "}";
470 }
471 
472 } // namespace STORAGE_CLIENT_NS
473 } // namespace storage
474 } // namespace cloud
475 } // namespace google