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