]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_iam_policy.cc
import ceph 14.2.5
[ceph.git] / ceph / src / rgw / rgw_iam_policy.cc
CommitLineData
31f18b77
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4
5#include <cstring>
11fdf7f2
TL
6#include <iostream>
7#include <regex>
31f18b77
FG
8#include <sstream>
9#include <stack>
10#include <utility>
11
11fdf7f2
TL
12#include <experimental/iterator>
13
31f18b77
FG
14#include "rapidjson/reader.h"
15
16#include "rgw_auth.h"
224ce89b 17#include <arpa/inet.h>
31f18b77
FG
18#include "rgw_iam_policy.h"
19
20namespace {
21constexpr int dout_subsys = ceph_subsys_rgw;
22}
23
24using std::bitset;
25using std::find;
26using std::int64_t;
27using std::move;
28using std::pair;
29using std::size_t;
30using std::string;
31using std::stringstream;
32using std::ostream;
33using std::uint16_t;
34using std::uint64_t;
35using std::unordered_map;
36
37using boost::container::flat_set;
11fdf7f2
TL
38using std::regex;
39using std::regex_constants::ECMAScript;
40using std::regex_constants::optimize;
41using std::regex_match;
42using std::smatch;
31f18b77
FG
43
44using rapidjson::BaseReaderHandler;
45using rapidjson::UTF8;
46using rapidjson::SizeType;
47using rapidjson::Reader;
48using rapidjson::kParseCommentsFlag;
49using rapidjson::kParseNumbersAsStringsFlag;
50using rapidjson::StringStream;
51using rapidjson::ParseResult;
52
53using rgw::auth::Principal;
54
55namespace rgw {
56namespace IAM {
57#include "rgw_iam_policy_keywords.frag.cc"
58
59struct actpair {
60 const char* name;
61 const uint64_t bit;
62};
63
31f18b77 64
31f18b77
FG
65
66static const actpair actpairs[] =
67{{ "s3:AbortMultipartUpload", s3AbortMultipartUpload },
68 { "s3:CreateBucket", s3CreateBucket },
69 { "s3:DeleteBucketPolicy", s3DeleteBucketPolicy },
70 { "s3:DeleteBucket", s3DeleteBucket },
71 { "s3:DeleteBucketWebsite", s3DeleteBucketWebsite },
72 { "s3:DeleteObject", s3DeleteObject },
73 { "s3:DeleteObjectVersion", s3DeleteObjectVersion },
224ce89b
WB
74 { "s3:DeleteObjectTagging", s3DeleteObjectTagging },
75 { "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging },
31f18b77
FG
76 { "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration },
77 { "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
78 { "s3:GetBucketAcl", s3GetBucketAcl },
79 { "s3:GetBucketCORS", s3GetBucketCORS },
80 { "s3:GetBucketLocation", s3GetBucketLocation },
81 { "s3:GetBucketLogging", s3GetBucketLogging },
82 { "s3:GetBucketNotification", s3GetBucketNotification },
83 { "s3:GetBucketPolicy", s3GetBucketPolicy },
84 { "s3:GetBucketRequestPayment", s3GetBucketRequestPayment },
85 { "s3:GetBucketTagging", s3GetBucketTagging },
86 { "s3:GetBucketVersioning", s3GetBucketVersioning },
87 { "s3:GetBucketWebsite", s3GetBucketWebsite },
88 { "s3:GetLifecycleConfiguration", s3GetLifecycleConfiguration },
eafe8130 89 { "s3:GetBucketObjectLockConfiguration", s3GetBucketObjectLockConfiguration },
31f18b77
FG
90 { "s3:GetObjectAcl", s3GetObjectAcl },
91 { "s3:GetObject", s3GetObject },
92 { "s3:GetObjectTorrent", s3GetObjectTorrent },
93 { "s3:GetObjectVersionAcl", s3GetObjectVersionAcl },
94 { "s3:GetObjectVersion", s3GetObjectVersion },
95 { "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent },
224ce89b
WB
96 { "s3:GetObjectTagging", s3GetObjectTagging },
97 { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
eafe8130
TL
98 { "s3:GetObjectRetention", s3GetObjectRetention},
99 { "s3:GetObjectLegalHold", s3GetObjectLegalHold},
31f18b77
FG
100 { "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
101 { "s3:ListAllMyBuckets", s3ListAllMyBuckets },
28e407b8 102 { "s3:ListBucketMultipartUploads", s3ListBucketMultipartUploads },
31f18b77
FG
103 { "s3:ListBucket", s3ListBucket },
104 { "s3:ListBucketVersions", s3ListBucketVersions },
105 { "s3:ListMultipartUploadParts", s3ListMultipartUploadParts },
106 { "s3:PutAccelerateConfiguration", s3PutAccelerateConfiguration },
107 { "s3:PutBucketAcl", s3PutBucketAcl },
108 { "s3:PutBucketCORS", s3PutBucketCORS },
109 { "s3:PutBucketLogging", s3PutBucketLogging },
110 { "s3:PutBucketNotification", s3PutBucketNotification },
111 { "s3:PutBucketPolicy", s3PutBucketPolicy },
112 { "s3:PutBucketRequestPayment", s3PutBucketRequestPayment },
113 { "s3:PutBucketTagging", s3PutBucketTagging },
114 { "s3:PutBucketVersioning", s3PutBucketVersioning },
115 { "s3:PutBucketWebsite", s3PutBucketWebsite },
116 { "s3:PutLifecycleConfiguration", s3PutLifecycleConfiguration },
eafe8130 117 { "s3:PutBucketObjectLockConfiguration", s3PutBucketObjectLockConfiguration },
31f18b77
FG
118 { "s3:PutObjectAcl", s3PutObjectAcl },
119 { "s3:PutObject", s3PutObject },
120 { "s3:PutObjectVersionAcl", s3PutObjectVersionAcl },
224ce89b
WB
121 { "s3:PutObjectTagging", s3PutObjectTagging },
122 { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
eafe8130
TL
123 { "s3:PutObjectRetention", s3PutObjectRetention },
124 { "s3:PutObjectLegalHold", s3PutObjectLegalHold },
125 { "s3:BypassGovernanceRetention", s3BypassGovernanceRetention },
31f18b77 126 { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
11fdf7f2
TL
127 { "s3:RestoreObject", s3RestoreObject },
128 { "iam:PutUserPolicy", iamPutUserPolicy },
129 { "iam:GetUserPolicy", iamGetUserPolicy },
130 { "iam:DeleteUserPolicy", iamDeleteUserPolicy },
131 { "iam:ListUserPolicies", iamListUserPolicies },
132 { "iam:CreateRole", iamCreateRole},
133 { "iam:DeleteRole", iamDeleteRole},
134 { "iam:GetRole", iamGetRole},
135 { "iam:ModifyRole", iamModifyRole},
136 { "iam:ListRoles", iamListRoles},
137 { "iam:PutRolePolicy", iamPutRolePolicy},
138 { "iam:GetRolePolicy", iamGetRolePolicy},
139 { "iam:ListRolePolicies", iamListRolePolicies},
140 { "iam:DeleteRolePolicy", iamDeleteRolePolicy},
141 { "sts:AssumeRole", stsAssumeRole},
142 { "sts:AssumeRoleWithWebIdentity", stsAssumeRoleWithWebIdentity},
143 { "sts:GetSessionToken", stsGetSessionToken},
144};
31f18b77
FG
145
146struct PolicyParser;
147
148const Keyword top[1]{"<Top>", TokenKind::pseudo, TokenID::Top, 0, false,
149 false};
150const Keyword cond_key[1]{"<Condition Key>", TokenKind::cond_key,
151 TokenID::CondKey, 0, true, false};
152
153struct ParseState {
154 PolicyParser* pp;
155 const Keyword* w;
156
157 bool arraying = false;
158 bool objecting = false;
c07f9fc5 159 bool cond_ifexists = false;
31f18b77
FG
160
161 void reset();
162
163 ParseState(PolicyParser* pp, const Keyword* w)
164 : pp(pp), w(w) {}
165
166 bool obj_start();
167
168 bool obj_end();
169
170 bool array_start() {
171 if (w->arrayable && !arraying) {
172 arraying = true;
173 return true;
174 }
175 return false;
176 }
177
178 bool array_end();
179
180 bool key(const char* s, size_t l);
181 bool do_string(CephContext* cct, const char* s, size_t l);
182 bool number(const char* str, size_t l);
183};
184
185// If this confuses you, look up the Curiously Recurring Template Pattern
186struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
187 keyword_hash tokens;
188 std::vector<ParseState> s;
189 CephContext* cct;
190 const string& tenant;
191 Policy& policy;
d2e6a577 192 uint32_t v = 0;
31f18b77
FG
193
194 uint32_t seen = 0;
195
196 uint32_t dex(TokenID in) const {
197 switch (in) {
198 case TokenID::Version:
199 return 0x1;
200 case TokenID::Id:
201 return 0x2;
202 case TokenID::Statement:
203 return 0x4;
204 case TokenID::Sid:
205 return 0x8;
206 case TokenID::Effect:
207 return 0x10;
208 case TokenID::Principal:
209 return 0x20;
210 case TokenID::NotPrincipal:
211 return 0x40;
212 case TokenID::Action:
213 return 0x80;
214 case TokenID::NotAction:
215 return 0x100;
216 case TokenID::Resource:
217 return 0x200;
218 case TokenID::NotResource:
219 return 0x400;
220 case TokenID::Condition:
221 return 0x800;
222 case TokenID::AWS:
223 return 0x1000;
224 case TokenID::Federated:
225 return 0x2000;
226 case TokenID::Service:
227 return 0x4000;
228 case TokenID::CanonicalUser:
229 return 0x8000;
230 default:
231 ceph_abort();
232 }
233 }
234 bool test(TokenID in) {
235 return seen & dex(in);
236 }
237 void set(TokenID in) {
238 seen |= dex(in);
d2e6a577
FG
239 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
240 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
241 dex(TokenID::Action) | dex(TokenID::NotAction) |
242 dex(TokenID::Resource) | dex(TokenID::NotResource) |
243 dex(TokenID::Condition) | dex(TokenID::AWS) |
244 dex(TokenID::Federated) | dex(TokenID::Service) |
245 dex(TokenID::CanonicalUser))) {
246 v |= dex(in);
c07f9fc5 247 }
31f18b77
FG
248 }
249 void set(std::initializer_list<TokenID> l) {
250 for (auto in : l) {
251 seen |= dex(in);
d2e6a577
FG
252 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
253 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
254 dex(TokenID::Action) | dex(TokenID::NotAction) |
255 dex(TokenID::Resource) | dex(TokenID::NotResource) |
256 dex(TokenID::Condition) | dex(TokenID::AWS) |
257 dex(TokenID::Federated) | dex(TokenID::Service) |
258 dex(TokenID::CanonicalUser))) {
259 v |= dex(in);
c07f9fc5 260 }
31f18b77
FG
261 }
262 }
263 void reset(TokenID in) {
264 seen &= ~dex(in);
d2e6a577
FG
265 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
266 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
267 dex(TokenID::Action) | dex(TokenID::NotAction) |
268 dex(TokenID::Resource) | dex(TokenID::NotResource) |
269 dex(TokenID::Condition) | dex(TokenID::AWS) |
270 dex(TokenID::Federated) | dex(TokenID::Service) |
271 dex(TokenID::CanonicalUser))) {
272 v &= ~dex(in);
c07f9fc5 273 }
31f18b77
FG
274 }
275 void reset(std::initializer_list<TokenID> l) {
276 for (auto in : l) {
277 seen &= ~dex(in);
d2e6a577
FG
278 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
279 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
280 dex(TokenID::Action) | dex(TokenID::NotAction) |
281 dex(TokenID::Resource) | dex(TokenID::NotResource) |
282 dex(TokenID::Condition) | dex(TokenID::AWS) |
283 dex(TokenID::Federated) | dex(TokenID::Service) |
284 dex(TokenID::CanonicalUser))) {
285 v &= ~dex(in);
c07f9fc5
FG
286 }
287 }
288 }
d2e6a577
FG
289 void reset(uint32_t& v) {
290 seen &= ~v;
291 v = 0;
31f18b77
FG
292 }
293
294 PolicyParser(CephContext* cct, const string& tenant, Policy& policy)
295 : cct(cct), tenant(tenant), policy(policy) {}
296 PolicyParser(const PolicyParser& policy) = delete;
297
298 bool StartObject() {
299 if (s.empty()) {
300 s.push_back({this, top});
301 s.back().objecting = true;
302 return true;
303 }
304
305 return s.back().obj_start();
306 }
307 bool EndObject(SizeType memberCount) {
308 if (s.empty()) {
309 return false;
310 }
31f18b77
FG
311 return s.back().obj_end();
312 }
313 bool Key(const char* str, SizeType length, bool copy) {
314 if (s.empty()) {
315 return false;
316 }
31f18b77
FG
317 return s.back().key(str, length);
318 }
319
320 bool String(const char* str, SizeType length, bool copy) {
321 if (s.empty()) {
322 return false;
323 }
31f18b77
FG
324 return s.back().do_string(cct, str, length);
325 }
326 bool RawNumber(const char* str, SizeType length, bool copy) {
327 if (s.empty()) {
328 return false;
329 }
330
331 return s.back().number(str, length);
332 }
333 bool StartArray() {
334 if (s.empty()) {
335 return false;
336 }
337
338 return s.back().array_start();
339 }
340 bool EndArray(SizeType) {
341 if (s.empty()) {
342 return false;
343 }
344
345 return s.back().array_end();
346 }
347
348 bool Default() {
349 return false;
350 }
351};
352
353
354// I really despise this misfeature of C++.
355//
356bool ParseState::obj_end() {
357 if (objecting) {
358 objecting = false;
359 if (!arraying) {
360 pp->s.pop_back();
361 } else {
362 reset();
363 }
364 return true;
365 }
366 return false;
367}
368
369bool ParseState::key(const char* s, size_t l) {
c07f9fc5
FG
370 auto token_len = l;
371 bool ifexists = false;
372 if (w->id == TokenID::Condition && w->kind == TokenKind::statement) {
373 static constexpr char IfExists[] = "IfExists";
374 if (boost::algorithm::ends_with(boost::string_view{s, l}, IfExists)) {
375 ifexists = true;
376 token_len -= sizeof(IfExists)-1;
377 }
378 }
379 auto k = pp->tokens.lookup(s, token_len);
31f18b77
FG
380
381 if (!k) {
382 if (w->kind == TokenKind::cond_op) {
d2e6a577 383 auto id = w->id;
31f18b77 384 auto& t = pp->policy.statements.back();
d2e6a577 385 auto c_ife = cond_ifexists;
31f18b77 386 pp->s.emplace_back(pp, cond_key);
d2e6a577 387 t.conditions.emplace_back(id, s, l, c_ife);
31f18b77
FG
388 return true;
389 } else {
390 return false;
391 }
392 }
393
394 // If the token we're going with belongs within the condition at the
395 // top of the stack and we haven't already encountered it, push it
396 // on the stack
31f18b77
FG
397 // Top
398 if ((((w->id == TokenID::Top) && (k->kind == TokenKind::top)) ||
399 // Statement
400 ((w->id == TokenID::Statement) && (k->kind == TokenKind::statement)) ||
401
402 /// Principal
403 ((w->id == TokenID::Principal || w->id == TokenID::NotPrincipal) &&
404 (k->kind == TokenKind::princ_type))) &&
405
406 // Check that it hasn't been encountered. Note that this
407 // conjoins with the run of disjunctions above.
408 !pp->test(k->id)) {
409 pp->set(k->id);
410 pp->s.emplace_back(pp, k);
411 return true;
412 } else if ((w->id == TokenID::Condition) &&
413 (k->kind == TokenKind::cond_op)) {
414 pp->s.emplace_back(pp, k);
c07f9fc5 415 pp->s.back().cond_ifexists = ifexists;
31f18b77
FG
416 return true;
417 }
418 return false;
419}
420
421// I should just rewrite a few helper functions to use iterators,
422// which will make all of this ever so much nicer.
11fdf7f2
TL
423static boost::optional<Principal> parse_principal(CephContext* cct, TokenID t,
424 string&& s) {
31f18b77
FG
425 // Wildcard!
426 if ((t == TokenID::AWS) && (s == "*")) {
427 return Principal::wildcard();
428
429 // Do nothing for now.
430 } else if (t == TokenID::CanonicalUser) {
431
11fdf7f2
TL
432 } // AWS and Federated ARNs
433 else if (t == TokenID::AWS || t == TokenID::Federated) {
b32b8144
FG
434 if (auto a = ARN::parse(s)) {
435 if (a->resource == "root") {
436 return Principal::tenant(std::move(a->account));
437 }
438
439 static const char rx_str[] = "([^/]*)/(.*)";
440 static const regex rx(rx_str, sizeof(rx_str) - 1,
11fdf7f2
TL
441 std::regex_constants::ECMAScript |
442 std::regex_constants::optimize);
b32b8144
FG
443 smatch match;
444 if (regex_match(a->resource, match, rx) && match.size() == 3) {
445 if (match[1] == "user") {
446 return Principal::user(std::move(a->account),
447 match[2]);
448 }
449
450 if (match[1] == "role") {
451 return Principal::role(std::move(a->account),
452 match[2]);
453 }
11fdf7f2
TL
454
455 if (match[1] == "oidc-provider") {
456 return Principal::oidc_provider(std::move(match[2]));
457 }
b32b8144
FG
458 }
459 } else {
31f18b77
FG
460 if (std::none_of(s.begin(), s.end(),
461 [](const char& c) {
462 return (c == ':') || (c == '/');
463 })) {
464 // Since tenants are simply prefixes, there's no really good
465 // way to see if one exists or not. So we return the thing and
466 // let them try to match against it.
467 return Principal::tenant(std::move(s));
468 }
469 }
31f18b77
FG
470 }
471
472 ldout(cct, 0) << "Supplied principal is discarded: " << s << dendl;
473 return boost::none;
474}
475
476bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
477 auto k = pp->tokens.lookup(s, l);
478 Policy& p = pp->policy;
11fdf7f2
TL
479 bool is_action = false;
480 bool is_validaction = false;
31f18b77
FG
481 Statement* t = p.statements.empty() ? nullptr : &(p.statements.back());
482
483 // Top level!
484 if ((w->id == TokenID::Version) && k &&
485 k->kind == TokenKind::version_key) {
486 p.version = static_cast<Version>(k->specific);
487 } else if (w->id == TokenID::Id) {
488 p.id = string(s, l);
489
490 // Statement
491
492 } else if (w->id == TokenID::Sid) {
493 t->sid.emplace(s, l);
b32b8144 494 } else if ((w->id == TokenID::Effect) && k &&
31f18b77
FG
495 k->kind == TokenKind::effect_key) {
496 t->effect = static_cast<Effect>(k->specific);
497 } else if (w->id == TokenID::Principal && s && *s == '*') {
498 t->princ.emplace(Principal::wildcard());
499 } else if (w->id == TokenID::NotPrincipal && s && *s == '*') {
500 t->noprinc.emplace(Principal::wildcard());
501 } else if ((w->id == TokenID::Action) ||
502 (w->id == TokenID::NotAction)) {
11fdf7f2
TL
503 is_action = true;
504 if (*s == '*') {
505 is_validaction = true;
506 (w->id == TokenID::Action ?
507 t->action = allValue : t->notaction = allValue);
508 } else {
509 for (auto& p : actpairs) {
510 if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) {
511 is_validaction = true;
512 (w->id == TokenID::Action ? t->action[p.bit] = 1 : t->notaction[p.bit] = 1);
513 }
514 if ((t->action & s3AllValue) == s3AllValue) {
515 t->action[s3All] = 1;
516 }
517 if ((t->notaction & s3AllValue) == s3AllValue) {
518 t->notaction[s3All] = 1;
519 }
520 if ((t->action & iamAllValue) == iamAllValue) {
521 t->action[iamAll] = 1;
522 }
523 if ((t->notaction & iamAllValue) == iamAllValue) {
524 t->notaction[iamAll] = 1;
525 }
526 if ((t->action & stsAllValue) == stsAllValue) {
527 t->action[stsAll] = 1;
528 }
529 if ((t->notaction & stsAllValue) == stsAllValue) {
530 t->notaction[stsAll] = 1;
531 }
31f18b77
FG
532 }
533 }
534 } else if (w->id == TokenID::Resource || w->id == TokenID::NotResource) {
535 auto a = ARN::parse({s, l}, true);
536 // You can't specify resources for someone ELSE'S account.
537 if (a && (a->account.empty() || a->account == pp->tenant ||
538 a->account == "*")) {
539 if (a->account.empty() || a->account == "*")
540 a->account = pp->tenant;
541 (w->id == TokenID::Resource ? t->resource : t->notresource)
542 .emplace(std::move(*a));
543 }
544 else
545 ldout(cct, 0) << "Supplied resource is discarded: " << string(s, l)
546 << dendl;
547 } else if (w->kind == TokenKind::cond_key) {
548 auto& t = pp->policy.statements.back();
549 t.conditions.back().vals.emplace_back(s, l);
550
551 // Principals
552
553 } else if (w->kind == TokenKind::princ_type) {
3efd9988
FG
554 if (pp->s.size() <= 1) {
555 return false;
556 }
31f18b77
FG
557 auto& pri = pp->s[pp->s.size() - 2].w->id == TokenID::Principal ?
558 t->princ : t->noprinc;
559
b32b8144
FG
560
561 if (auto o = parse_principal(pp->cct, w->id, string(s, l))) {
31f18b77 562 pri.emplace(std::move(*o));
b32b8144 563 }
31f18b77
FG
564
565 // Failure
566
567 } else {
568 return false;
569 }
570
571 if (!arraying) {
572 pp->s.pop_back();
573 }
574
11fdf7f2
TL
575 if (is_action && !is_validaction){
576 return false;
577 }
578
31f18b77
FG
579 return true;
580}
581
582bool ParseState::number(const char* s, size_t l) {
583 // Top level!
584 if (w->kind == TokenKind::cond_key) {
585 auto& t = pp->policy.statements.back();
586 t.conditions.back().vals.emplace_back(s, l);
587
588 // Failure
589
590 } else {
591 return false;
592 }
593
594 if (!arraying) {
595 pp->s.pop_back();
596 }
597
598 return true;
599}
600
601void ParseState::reset() {
c07f9fc5 602 pp->reset(pp->v);
31f18b77
FG
603}
604
605bool ParseState::obj_start() {
606 if (w->objectable && !objecting) {
607 objecting = true;
608 if (w->id == TokenID::Statement) {
11fdf7f2 609 pp->policy.statements.emplace_back();
31f18b77
FG
610 }
611
612 return true;
613 }
614
615 return false;
616}
617
618
619bool ParseState::array_end() {
620 if (arraying && !objecting) {
621 pp->s.pop_back();
622 return true;
623 }
624
625 return false;
626}
627
628ostream& operator <<(ostream& m, const MaskedIP& ip) {
629 // I have a theory about why std::bitset is the way it is.
630 if (ip.v6) {
b32b8144
FG
631 for (int i = 7; i >= 0; --i) {
632 uint16_t hextet = 0;
633 for (int j = 15; j >= 0; --j) {
634 hextet |= (ip.addr[(i * 16) + j] << j);
31f18b77 635 }
b32b8144 636 m << hex << (unsigned int) hextet;
31f18b77 637 if (i != 0) {
b32b8144 638 m << ":";
31f18b77
FG
639 }
640 }
641 } else {
642 // It involves Satan.
643 for (int i = 3; i >= 0; --i) {
644 uint8_t b = 0;
645 for (int j = 7; j >= 0; --j) {
646 b |= (ip.addr[(i * 8) + j] << j);
647 }
b32b8144 648 m << (unsigned int) b;
31f18b77
FG
649 if (i != 0) {
650 m << ".";
651 }
652 }
653 }
b32b8144 654 m << "/" << dec << ip.prefix;
31f18b77
FG
655 // It would explain a lot
656 return m;
657}
658
31f18b77
FG
659bool Condition::eval(const Environment& env) const {
660 auto i = env.find(key);
661 if (op == TokenID::Null) {
662 return i == env.end() ? true : false;
663 }
664
665 if (i == env.end()) {
c07f9fc5 666 return ifexists;
31f18b77
FG
667 }
668 const auto& s = i->second;
669
670 switch (op) {
671 // String!
672 case TokenID::StringEquals:
673 return orrible(std::equal_to<std::string>(), s, vals);
674
675 case TokenID::StringNotEquals:
11fdf7f2 676 return orrible(std::not_fn(std::equal_to<std::string>()),
31f18b77
FG
677 s, vals);
678
679 case TokenID::StringEqualsIgnoreCase:
680 return orrible(ci_equal_to(), s, vals);
681
682 case TokenID::StringNotEqualsIgnoreCase:
11fdf7f2 683 return orrible(std::not_fn(ci_equal_to()), s, vals);
31f18b77 684
31f18b77 685 case TokenID::StringLike:
d2e6a577
FG
686 return orrible(string_like(), s, vals);
687
31f18b77 688 case TokenID::StringNotLike:
11fdf7f2 689 return orrible(std::not_fn(string_like()), s, vals);
31f18b77
FG
690
691 // Numeric
692 case TokenID::NumericEquals:
693 return shortible(std::equal_to<double>(), as_number, s, vals);
694
695 case TokenID::NumericNotEquals:
11fdf7f2 696 return shortible(std::not_fn(std::equal_to<double>()),
31f18b77
FG
697 as_number, s, vals);
698
699
700 case TokenID::NumericLessThan:
701 return shortible(std::less<double>(), as_number, s, vals);
702
703
704 case TokenID::NumericLessThanEquals:
705 return shortible(std::less_equal<double>(), as_number, s, vals);
706
707 case TokenID::NumericGreaterThan:
708 return shortible(std::greater<double>(), as_number, s, vals);
709
710 case TokenID::NumericGreaterThanEquals:
711 return shortible(std::greater_equal<double>(), as_number, s, vals);
712
713 // Date!
714 case TokenID::DateEquals:
715 return shortible(std::equal_to<ceph::real_time>(), as_date, s, vals);
716
717 case TokenID::DateNotEquals:
11fdf7f2 718 return shortible(std::not_fn(std::equal_to<ceph::real_time>()),
31f18b77
FG
719 as_date, s, vals);
720
721 case TokenID::DateLessThan:
722 return shortible(std::less<ceph::real_time>(), as_date, s, vals);
723
724
725 case TokenID::DateLessThanEquals:
726 return shortible(std::less_equal<ceph::real_time>(), as_date, s, vals);
727
728 case TokenID::DateGreaterThan:
729 return shortible(std::greater<ceph::real_time>(), as_date, s, vals);
730
731 case TokenID::DateGreaterThanEquals:
732 return shortible(std::greater_equal<ceph::real_time>(), as_date, s,
733 vals);
734
735 // Bool!
736 case TokenID::Bool:
737 return shortible(std::equal_to<bool>(), as_bool, s, vals);
738
739 // Binary!
740 case TokenID::BinaryEquals:
741 return shortible(std::equal_to<ceph::bufferlist>(), as_binary, s,
742 vals);
743
744 // IP Address!
745 case TokenID::IpAddress:
746 return shortible(std::equal_to<MaskedIP>(), as_network, s, vals);
747
748 case TokenID::NotIpAddress:
b32b8144
FG
749 {
750 auto xc = as_network(s);
751 if (!xc) {
752 return false;
753 }
754
755 for (const string& d : vals) {
756 auto xd = as_network(d);
757 if (!xd) {
758 continue;
759 }
760
761 if (xc == xd) {
762 return false;
763 }
764 }
765 return true;
766 }
31f18b77
FG
767
768#if 0
769 // Amazon Resource Names! (Does S3 need this?)
770 TokenID::ArnEquals, TokenID::ArnNotEquals, TokenID::ArnLike,
771 TokenID::ArnNotLike,
772#endif
773
774 default:
775 return false;
776 }
777}
778
11fdf7f2 779boost::optional<MaskedIP> Condition::as_network(const string& s) {
31f18b77
FG
780 MaskedIP m;
781 if (s.empty()) {
11fdf7f2 782 return boost::none;
31f18b77
FG
783 }
784
b32b8144 785 m.v6 = (s.find(':') == string::npos) ? false : true;
11fdf7f2 786
31f18b77
FG
787 auto slash = s.find('/');
788 if (slash == string::npos) {
789 m.prefix = m.v6 ? 128 : 32;
790 } else {
791 char* end = 0;
792 m.prefix = strtoul(s.data() + slash + 1, &end, 10);
793 if (*end != 0 || (m.v6 && m.prefix > 128) ||
794 (!m.v6 && m.prefix > 32)) {
11fdf7f2 795 return boost::none;
31f18b77
FG
796 }
797 }
798
799 string t;
800 auto p = &s;
801
802 if (slash != string::npos) {
803 t.assign(s, 0, slash);
804 p = &t;
805 }
806
807 if (m.v6) {
b32b8144 808 struct in6_addr a;
31f18b77 809 if (inet_pton(AF_INET6, p->c_str(), static_cast<void*>(&a)) != 1) {
11fdf7f2 810 return boost::none;
31f18b77
FG
811 }
812
b32b8144
FG
813 m.addr |= Address(a.s6_addr[15]) << 0;
814 m.addr |= Address(a.s6_addr[14]) << 8;
815 m.addr |= Address(a.s6_addr[13]) << 16;
816 m.addr |= Address(a.s6_addr[12]) << 24;
817 m.addr |= Address(a.s6_addr[11]) << 32;
818 m.addr |= Address(a.s6_addr[10]) << 40;
819 m.addr |= Address(a.s6_addr[9]) << 48;
820 m.addr |= Address(a.s6_addr[8]) << 56;
821 m.addr |= Address(a.s6_addr[7]) << 64;
822 m.addr |= Address(a.s6_addr[6]) << 72;
823 m.addr |= Address(a.s6_addr[5]) << 80;
824 m.addr |= Address(a.s6_addr[4]) << 88;
825 m.addr |= Address(a.s6_addr[3]) << 96;
826 m.addr |= Address(a.s6_addr[2]) << 104;
827 m.addr |= Address(a.s6_addr[1]) << 112;
828 m.addr |= Address(a.s6_addr[0]) << 120;
31f18b77 829 } else {
b32b8144 830 struct in_addr a;
31f18b77 831 if (inet_pton(AF_INET, p->c_str(), static_cast<void*>(&a)) != 1) {
11fdf7f2 832 return boost::none;
31f18b77 833 }
b32b8144
FG
834
835 m.addr = ntohl(a.s_addr);
31f18b77
FG
836 }
837
b32b8144 838 return m;
31f18b77
FG
839}
840
841namespace {
842const char* condop_string(const TokenID t) {
843 switch (t) {
844 case TokenID::StringEquals:
845 return "StringEquals";
846
847 case TokenID::StringNotEquals:
848 return "StringNotEquals";
849
850 case TokenID::StringEqualsIgnoreCase:
851 return "StringEqualsIgnoreCase";
852
853 case TokenID::StringNotEqualsIgnoreCase:
854 return "StringNotEqualsIgnoreCase";
855
856 case TokenID::StringLike:
857 return "StringLike";
858
859 case TokenID::StringNotLike:
860 return "StringNotLike";
861
862 // Numeric!
863 case TokenID::NumericEquals:
864 return "NumericEquals";
865
866 case TokenID::NumericNotEquals:
867 return "NumericNotEquals";
868
869 case TokenID::NumericLessThan:
870 return "NumericLessThan";
871
872 case TokenID::NumericLessThanEquals:
873 return "NumericLessThanEquals";
874
875 case TokenID::NumericGreaterThan:
876 return "NumericGreaterThan";
877
878 case TokenID::NumericGreaterThanEquals:
879 return "NumericGreaterThanEquals";
880
881 case TokenID::DateEquals:
882 return "DateEquals";
883
884 case TokenID::DateNotEquals:
885 return "DateNotEquals";
886
887 case TokenID::DateLessThan:
888 return "DateLessThan";
889
890 case TokenID::DateLessThanEquals:
891 return "DateLessThanEquals";
892
893 case TokenID::DateGreaterThan:
894 return "DateGreaterThan";
895
896 case TokenID::DateGreaterThanEquals:
897 return "DateGreaterThanEquals";
898
899 case TokenID::Bool:
900 return "Bool";
901
902 case TokenID::BinaryEquals:
903 return "BinaryEquals";
904
905 case TokenID::IpAddress:
906 return "case TokenID::IpAddress";
907
908 case TokenID::NotIpAddress:
909 return "NotIpAddress";
910
911 case TokenID::ArnEquals:
912 return "ArnEquals";
913
914 case TokenID::ArnNotEquals:
915 return "ArnNotEquals";
916
917 case TokenID::ArnLike:
918 return "ArnLike";
919
920 case TokenID::ArnNotLike:
921 return "ArnNotLike";
922
923 case TokenID::Null:
924 return "Null";
925
926 default:
927 return "InvalidConditionOperator";
928 }
929}
930
931template<typename Iterator>
932ostream& print_array(ostream& m, Iterator begin, Iterator end) {
933 if (begin == end) {
11fdf7f2 934 m << "[]";
31f18b77 935 } else {
31f18b77 936 m << "[ ";
11fdf7f2
TL
937 std::copy(begin, end, std::experimental::make_ostream_joiner(m, ", "));
938 m << " ]";
31f18b77 939 }
31f18b77
FG
940 return m;
941}
11fdf7f2
TL
942
943template<typename Iterator>
944ostream& print_dict(ostream& m, Iterator begin, Iterator end) {
945 m << "{ ";
946 std::copy(begin, end, std::experimental::make_ostream_joiner(m, ", "));
947 m << " }";
948 return m;
949}
950
31f18b77
FG
951}
952
953ostream& operator <<(ostream& m, const Condition& c) {
11fdf7f2 954 m << condop_string(c.op);
31f18b77
FG
955 if (c.ifexists) {
956 m << "IfExists";
957 }
958 m << ": { " << c.key;
959 print_array(m, c.vals.cbegin(), c.vals.cend());
11fdf7f2 960 return m << " }";
31f18b77
FG
961}
962
963Effect Statement::eval(const Environment& e,
11fdf7f2 964 boost::optional<const rgw::auth::Identity&> ida,
31f18b77 965 uint64_t act, const ARN& res) const {
11fdf7f2
TL
966 if (ida) {
967 if (!princ.empty() && !ida->is_identity(princ)) {
968 return Effect::Pass;
969 } else if (!noprinc.empty() && ida->is_identity(noprinc)) {
970 return Effect::Pass;
971 }
31f18b77
FG
972 }
973
11fdf7f2
TL
974 if (!resource.empty()) {
975 if (!std::any_of(resource.begin(), resource.end(),
976 [&res](const ARN& pattern) {
977 return pattern.match(res);
978 })) {
979 return Effect::Pass;
980 }
981 } else if (!notresource.empty()) {
982 if (std::any_of(notresource.begin(), notresource.end(),
983 [&res](const ARN& pattern) {
984 return pattern.match(res);
985 })) {
986 return Effect::Pass;
987 }
31f18b77
FG
988 }
989
11fdf7f2 990 if (!(action[act] == 1) || (notaction[act] == 1)) {
31f18b77
FG
991 return Effect::Pass;
992 }
993
994 if (std::all_of(conditions.begin(),
995 conditions.end(),
996 [&e](const Condition& c) { return c.eval(e);})) {
997 return effect;
998 }
999
1000 return Effect::Pass;
1001}
1002
11fdf7f2
TL
1003Effect Statement::eval_principal(const Environment& e,
1004 boost::optional<const rgw::auth::Identity&> ida) const {
1005 if (ida) {
1006 if (princ.empty() && noprinc.empty()) {
1007 return Effect::Deny;
1008 }
1009 if (!princ.empty() && !ida->is_identity(princ)) {
1010 return Effect::Deny;
1011 } else if (!noprinc.empty() && ida->is_identity(noprinc)) {
1012 return Effect::Deny;
1013 }
1014 }
1015 return Effect::Allow;
1016}
1017
1018Effect Statement::eval_conditions(const Environment& e) const {
1019 if (std::all_of(conditions.begin(),
1020 conditions.end(),
1021 [&e](const Condition& c) { return c.eval(e);})) {
1022 return Effect::Allow;
1023 }
1024 return Effect::Deny;
1025}
1026
31f18b77
FG
1027namespace {
1028const char* action_bit_string(uint64_t action) {
1029 switch (action) {
1030 case s3GetObject:
1031 return "s3:GetObject";
1032
1033 case s3GetObjectVersion:
1034 return "s3:GetObjectVersion";
1035
1036 case s3PutObject:
1037 return "s3:PutObject";
1038
1039 case s3GetObjectAcl:
1040 return "s3:GetObjectAcl";
1041
1042 case s3GetObjectVersionAcl:
1043 return "s3:GetObjectVersionAcl";
1044
1045 case s3PutObjectAcl:
1046 return "s3:PutObjectAcl";
1047
1048 case s3PutObjectVersionAcl:
1049 return "s3:PutObjectVersionAcl";
1050
1051 case s3DeleteObject:
1052 return "s3:DeleteObject";
1053
1054 case s3DeleteObjectVersion:
1055 return "s3:DeleteObjectVersion";
1056
1057 case s3ListMultipartUploadParts:
1058 return "s3:ListMultipartUploadParts";
1059
1060 case s3AbortMultipartUpload:
1061 return "s3:AbortMultipartUpload";
1062
1063 case s3GetObjectTorrent:
1064 return "s3:GetObjectTorrent";
1065
1066 case s3GetObjectVersionTorrent:
1067 return "s3:GetObjectVersionTorrent";
1068
1069 case s3RestoreObject:
1070 return "s3:RestoreObject";
1071
1072 case s3CreateBucket:
1073 return "s3:CreateBucket";
1074
1075 case s3DeleteBucket:
1076 return "s3:DeleteBucket";
1077
1078 case s3ListBucket:
1079 return "s3:ListBucket";
1080
1081 case s3ListBucketVersions:
1082 return "s3:ListBucketVersions";
1083 case s3ListAllMyBuckets:
1084 return "s3:ListAllMyBuckets";
1085
28e407b8
AA
1086 case s3ListBucketMultipartUploads:
1087 return "s3:ListBucketMultipartUploads";
31f18b77
FG
1088
1089 case s3GetAccelerateConfiguration:
1090 return "s3:GetAccelerateConfiguration";
1091
1092 case s3PutAccelerateConfiguration:
1093 return "s3:PutAccelerateConfiguration";
1094
1095 case s3GetBucketAcl:
1096 return "s3:GetBucketAcl";
1097
1098 case s3PutBucketAcl:
1099 return "s3:PutBucketAcl";
1100
1101 case s3GetBucketCORS:
1102 return "s3:GetBucketCORS";
1103
1104 case s3PutBucketCORS:
1105 return "s3:PutBucketCORS";
1106
1107 case s3GetBucketVersioning:
1108 return "s3:GetBucketVersioning";
1109
1110 case s3PutBucketVersioning:
1111 return "s3:PutBucketVersioning";
1112
1113 case s3GetBucketRequestPayment:
1114 return "s3:GetBucketRequestPayment";
1115
1116 case s3PutBucketRequestPayment:
1117 return "s3:PutBucketRequestPayment";
1118
1119 case s3GetBucketLocation:
1120 return "s3:GetBucketLocation";
1121
1122 case s3GetBucketPolicy:
1123 return "s3:GetBucketPolicy";
1124
1125 case s3DeleteBucketPolicy:
1126 return "s3:DeleteBucketPolicy";
1127
1128 case s3PutBucketPolicy:
1129 return "s3:PutBucketPolicy";
1130
1131 case s3GetBucketNotification:
1132 return "s3:GetBucketNotification";
1133
1134 case s3PutBucketNotification:
1135 return "s3:PutBucketNotification";
1136
1137 case s3GetBucketLogging:
1138 return "s3:GetBucketLogging";
1139
1140 case s3PutBucketLogging:
1141 return "s3:PutBucketLogging";
1142
1143 case s3GetBucketTagging:
1144 return "s3:GetBucketTagging";
1145
1146 case s3PutBucketTagging:
1147 return "s3:PutBucketTagging";
1148
1149 case s3GetBucketWebsite:
1150 return "s3:GetBucketWebsite";
1151
1152 case s3PutBucketWebsite:
1153 return "s3:PutBucketWebsite";
1154
1155 case s3DeleteBucketWebsite:
1156 return "s3:DeleteBucketWebsite";
1157
1158 case s3GetLifecycleConfiguration:
1159 return "s3:GetLifecycleConfiguration";
1160
1161 case s3PutLifecycleConfiguration:
1162 return "s3:PutLifecycleConfiguration";
1163
1164 case s3PutReplicationConfiguration:
1165 return "s3:PutReplicationConfiguration";
1166
1167 case s3GetReplicationConfiguration:
1168 return "s3:GetReplicationConfiguration";
1169
1170 case s3DeleteReplicationConfiguration:
1171 return "s3:DeleteReplicationConfiguration";
224ce89b
WB
1172
1173 case s3PutObjectTagging:
1174 return "s3:PutObjectTagging";
1175
1176 case s3PutObjectVersionTagging:
1177 return "s3:PutObjectVersionTagging";
1178
1179 case s3GetObjectTagging:
1180 return "s3:GetObjectTagging";
1181
1182 case s3GetObjectVersionTagging:
1183 return "s3:GetObjectVersionTagging";
1184
1185 case s3DeleteObjectTagging:
1186 return "s3:DeleteObjectTagging";
1187
1188 case s3DeleteObjectVersionTagging:
1189 return "s3:DeleteObjectVersionTagging";
11fdf7f2 1190
eafe8130
TL
1191 case s3PutBucketObjectLockConfiguration:
1192 return "s3:PutBucketObjectLockConfiguration";
1193
1194 case s3GetBucketObjectLockConfiguration:
1195 return "s3:GetBucketObjectLockConfiguration";
1196
1197 case s3PutObjectRetention:
1198 return "s3:PutObjectRetention";
1199
1200 case s3GetObjectRetention:
1201 return "s3:GetObjectRetention";
1202
1203 case s3PutObjectLegalHold:
1204 return "s3:PutObjectLegalHold";
1205
1206 case s3GetObjectLegalHold:
1207 return "s3:GetObjectLegalHold";
1208
1209 case s3BypassGovernanceRetention:
1210 return "s3:BypassGovernanceRetention";
1211
11fdf7f2
TL
1212 case iamPutUserPolicy:
1213 return "iam:PutUserPolicy";
1214
1215 case iamGetUserPolicy:
1216 return "iam:GetUserPolicy";
1217
1218 case iamListUserPolicies:
1219 return "iam:ListUserPolicies";
1220
1221 case iamDeleteUserPolicy:
1222 return "iam:DeleteUserPolicy";
1223
1224 case iamCreateRole:
1225 return "iam:CreateRole";
1226
1227 case iamDeleteRole:
1228 return "iam:DeleteRole";
1229
1230 case iamGetRole:
1231 return "iam:GetRole";
1232
1233 case iamModifyRole:
1234 return "iam:ModifyRole";
1235
1236 case iamListRoles:
1237 return "iam:ListRoles";
1238
1239 case iamPutRolePolicy:
1240 return "iam:PutRolePolicy";
1241
1242 case iamGetRolePolicy:
1243 return "iam:GetRolePolicy";
1244
1245 case iamListRolePolicies:
1246 return "iam:ListRolePolicies";
1247
1248 case iamDeleteRolePolicy:
1249 return "iam:DeleteRolePolicy";
1250
1251 case stsAssumeRole:
1252 return "sts:AssumeRole";
1253
1254 case stsAssumeRoleWithWebIdentity:
1255 return "sts:AssumeRoleWithWebIdentity";
1256
1257 case stsGetSessionToken:
1258 return "sts:GetSessionToken";
31f18b77
FG
1259 }
1260 return "s3Invalid";
1261}
1262
11fdf7f2 1263ostream& print_actions(ostream& m, const Action_t a) {
31f18b77
FG
1264 bool begun = false;
1265 m << "[ ";
11fdf7f2
TL
1266 for (auto i = 0U; i < allCount; ++i) {
1267 if (a[i] == 1) {
31f18b77 1268 if (begun) {
11fdf7f2 1269 m << ", ";
31f18b77 1270 } else {
11fdf7f2 1271 begun = true;
31f18b77 1272 }
11fdf7f2 1273 m << action_bit_string(i);
31f18b77
FG
1274 }
1275 }
1276 if (begun) {
1277 m << " ]";
1278 } else {
1279 m << "]";
1280 }
1281 return m;
1282}
1283}
1284
1285ostream& operator <<(ostream& m, const Statement& s) {
1286 m << "{ ";
1287 if (s.sid) {
1288 m << "Sid: " << *s.sid << ", ";
1289 }
1290 if (!s.princ.empty()) {
1291 m << "Principal: ";
11fdf7f2 1292 print_dict(m, s.princ.cbegin(), s.princ.cend());
31f18b77
FG
1293 m << ", ";
1294 }
1295 if (!s.noprinc.empty()) {
1296 m << "NotPrincipal: ";
11fdf7f2 1297 print_dict(m, s.noprinc.cbegin(), s.noprinc.cend());
31f18b77
FG
1298 m << ", ";
1299 }
1300
1301 m << "Effect: " <<
1302 (s.effect == Effect::Allow ?
1303 (const char*) "Allow" :
1304 (const char*) "Deny");
1305
11fdf7f2 1306 if (s.action.any() || s.notaction.any() || !s.resource.empty() ||
31f18b77
FG
1307 !s.notresource.empty() || !s.conditions.empty()) {
1308 m << ", ";
1309 }
1310
11fdf7f2 1311 if (s.action.any()) {
31f18b77
FG
1312 m << "Action: ";
1313 print_actions(m, s.action);
1314
11fdf7f2 1315 if (s.notaction.any() || !s.resource.empty() ||
31f18b77
FG
1316 !s.notresource.empty() || !s.conditions.empty()) {
1317 m << ", ";
1318 }
1319 }
1320
11fdf7f2 1321 if (s.notaction.any()) {
31f18b77
FG
1322 m << "NotAction: ";
1323 print_actions(m, s.notaction);
1324
1325 if (!s.resource.empty() || !s.notresource.empty() ||
1326 !s.conditions.empty()) {
1327 m << ", ";
1328 }
1329 }
1330
1331 if (!s.resource.empty()) {
1332 m << "Resource: ";
1333 print_array(m, s.resource.cbegin(), s.resource.cend());
1334
1335 if (!s.notresource.empty() || !s.conditions.empty()) {
1336 m << ", ";
1337 }
1338 }
1339
1340 if (!s.notresource.empty()) {
1341 m << "NotResource: ";
1342 print_array(m, s.notresource.cbegin(), s.notresource.cend());
1343
1344 if (!s.conditions.empty()) {
1345 m << ", ";
1346 }
1347 }
1348
1349 if (!s.conditions.empty()) {
1350 m << "Condition: ";
11fdf7f2 1351 print_dict(m, s.conditions.cbegin(), s.conditions.cend());
31f18b77
FG
1352 }
1353
1354 return m << " }";
1355}
1356
31f18b77
FG
1357Policy::Policy(CephContext* cct, const string& tenant,
1358 const bufferlist& _text)
1359 : text(_text.to_str()) {
1360 StringStream ss(text.data());
1361 PolicyParser pp(cct, tenant, *this);
1362 auto pr = Reader{}.Parse<kParseNumbersAsStringsFlag |
1363 kParseCommentsFlag>(ss, pp);
1364 if (!pr) {
1365 throw PolicyParseException(std::move(pr));
1366 }
1367}
1368
1369Effect Policy::eval(const Environment& e,
11fdf7f2 1370 boost::optional<const rgw::auth::Identity&> ida,
31f18b77
FG
1371 std::uint64_t action, const ARN& resource) const {
1372 auto allowed = false;
1373 for (auto& s : statements) {
1374 auto g = s.eval(e, ida, action, resource);
1375 if (g == Effect::Deny) {
1376 return g;
1377 } else if (g == Effect::Allow) {
1378 allowed = true;
1379 }
1380 }
1381 return allowed ? Effect::Allow : Effect::Pass;
1382}
1383
11fdf7f2
TL
1384Effect Policy::eval_principal(const Environment& e,
1385 boost::optional<const rgw::auth::Identity&> ida) const {
1386 auto allowed = false;
1387 for (auto& s : statements) {
1388 auto g = s.eval_principal(e, ida);
1389 if (g == Effect::Deny) {
1390 return g;
1391 } else if (g == Effect::Allow) {
1392 allowed = true;
1393 }
1394 }
1395 return allowed ? Effect::Allow : Effect::Deny;
1396}
1397
1398Effect Policy::eval_conditions(const Environment& e) const {
1399 auto allowed = false;
1400 for (auto& s : statements) {
1401 auto g = s.eval_conditions(e);
1402 if (g == Effect::Deny) {
1403 return g;
1404 } else if (g == Effect::Allow) {
1405 allowed = true;
1406 }
1407 }
1408 return allowed ? Effect::Allow : Effect::Deny;
1409}
1410
31f18b77
FG
1411ostream& operator <<(ostream& m, const Policy& p) {
1412 m << "{ Version: "
1413 << (p.version == Version::v2008_10_17 ? "2008-10-17" : "2012-10-17");
1414
1415 if (p.id || !p.statements.empty()) {
1416 m << ", ";
1417 }
1418
1419 if (p.id) {
1420 m << "Id: " << *p.id;
1421 if (!p.statements.empty()) {
1422 m << ", ";
1423 }
1424 }
1425
1426 if (!p.statements.empty()) {
1427 m << "Statements: ";
1428 print_array(m, p.statements.cbegin(), p.statements.cend());
1429 m << ", ";
1430 }
1431 return m << " }";
1432}
1433
31f18b77
FG
1434}
1435}