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