]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_iam_policy.h
update sources to v12.1.3
[ceph.git] / ceph / src / rgw / rgw_iam_policy.h
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#ifndef CEPH_RGW_IAM_POLICY_H
5#define CEPH_RGW_IAM_POLICY_H
6
7#include <bitset>
8#include <chrono>
9#include <cstdint>
10#include <iostream>
11#include <string>
12
13#include <boost/algorithm/string/predicate.hpp>
14#include <boost/container/flat_map.hpp>
15#include <boost/container/flat_set.hpp>
16#include <boost/optional.hpp>
17#include <boost/thread/shared_mutex.hpp>
18#include <boost/utility/string_ref.hpp>
19#include <boost/variant.hpp>
20
21#include "common/ceph_time.h"
22#include "common/iso_8601.h"
23
24#include "rapidjson/error/error.h"
25#include "rapidjson/error/en.h"
26
c07f9fc5 27#include "rgw_acl.h"
31f18b77
FG
28#include "rgw_basic_types.h"
29#include "rgw_iam_policy_keywords.h"
d2e6a577 30#include "rgw_string.h"
31f18b77
FG
31
32#include "include/assert.h" // razzin' frazzin' ...grrr.
33
34class RGWRados;
35namespace rgw {
36namespace auth {
37class Identity;
38}
39}
40struct rgw_obj;
41struct rgw_bucket;
42
43namespace rgw {
44namespace IAM {
45static constexpr std::uint64_t s3None = 0;
46static constexpr std::uint64_t s3GetObject = 1ULL << 0;
47static constexpr std::uint64_t s3GetObjectVersion = 1ULL << 1;
48static constexpr std::uint64_t s3PutObject = 1ULL << 2;
49static constexpr std::uint64_t s3GetObjectAcl = 1ULL << 3;
50static constexpr std::uint64_t s3GetObjectVersionAcl = 1ULL << 4;
51static constexpr std::uint64_t s3PutObjectAcl = 1ULL << 5;
52static constexpr std::uint64_t s3PutObjectVersionAcl = 1ULL << 6;
53static constexpr std::uint64_t s3DeleteObject = 1ULL << 7;
54static constexpr std::uint64_t s3DeleteObjectVersion = 1ULL << 8;
55static constexpr std::uint64_t s3ListMultipartUploadParts = 1ULL << 9;
56static constexpr std::uint64_t s3AbortMultipartUpload = 1ULL << 10;
57static constexpr std::uint64_t s3GetObjectTorrent = 1ULL << 11;
58static constexpr std::uint64_t s3GetObjectVersionTorrent = 1ULL << 12;
59static constexpr std::uint64_t s3RestoreObject = 1ULL << 13;
60static constexpr std::uint64_t s3CreateBucket = 1ULL << 14;
61static constexpr std::uint64_t s3DeleteBucket = 1ULL << 15;
62static constexpr std::uint64_t s3ListBucket = 1ULL << 16;
63static constexpr std::uint64_t s3ListBucketVersions = 1ULL << 17;
64static constexpr std::uint64_t s3ListAllMyBuckets = 1ULL << 18;
65static constexpr std::uint64_t s3ListBucketMultiPartUploads = 1ULL << 19;
66static constexpr std::uint64_t s3GetAccelerateConfiguration = 1ULL << 20;
67static constexpr std::uint64_t s3PutAccelerateConfiguration = 1ULL << 21;
68static constexpr std::uint64_t s3GetBucketAcl = 1ULL << 22;
69static constexpr std::uint64_t s3PutBucketAcl = 1ULL << 23;
70static constexpr std::uint64_t s3GetBucketCORS = 1ULL << 24;
71static constexpr std::uint64_t s3PutBucketCORS = 1ULL << 25;
72static constexpr std::uint64_t s3GetBucketVersioning = 1ULL << 26;
73static constexpr std::uint64_t s3PutBucketVersioning = 1ULL << 27;
74static constexpr std::uint64_t s3GetBucketRequestPayment = 1ULL << 28;
75static constexpr std::uint64_t s3PutBucketRequestPayment = 1ULL << 29;
76static constexpr std::uint64_t s3GetBucketLocation = 1ULL << 30;
77static constexpr std::uint64_t s3GetBucketPolicy = 1ULL << 31;
78static constexpr std::uint64_t s3DeleteBucketPolicy = 1ULL << 32;
79static constexpr std::uint64_t s3PutBucketPolicy = 1ULL << 33;
80static constexpr std::uint64_t s3GetBucketNotification = 1ULL << 34;
81static constexpr std::uint64_t s3PutBucketNotification = 1ULL << 35;
82static constexpr std::uint64_t s3GetBucketLogging = 1ULL << 36;
83static constexpr std::uint64_t s3PutBucketLogging = 1ULL << 37;
84static constexpr std::uint64_t s3GetBucketTagging = 1ULL << 38;
85static constexpr std::uint64_t s3PutBucketTagging = 1ULL << 39;
86static constexpr std::uint64_t s3GetBucketWebsite = 1ULL << 40;
87static constexpr std::uint64_t s3PutBucketWebsite = 1ULL << 41;
88static constexpr std::uint64_t s3DeleteBucketWebsite = 1ULL << 42;
89static constexpr std::uint64_t s3GetLifecycleConfiguration = 1ULL << 43;
90static constexpr std::uint64_t s3PutLifecycleConfiguration = 1ULL << 44;
91static constexpr std::uint64_t s3PutReplicationConfiguration = 1ULL << 45;
92static constexpr std::uint64_t s3GetReplicationConfiguration = 1ULL << 46;
93static constexpr std::uint64_t s3DeleteReplicationConfiguration = 1ULL << 47;
224ce89b
WB
94static constexpr std::uint64_t s3GetObjectTagging = 1ULL << 48;
95static constexpr std::uint64_t s3PutObjectTagging = 1ULL << 49;
96static constexpr std::uint64_t s3DeleteObjectTagging = 1ULL << 50;
97static constexpr std::uint64_t s3GetObjectVersionTagging = 1ULL << 51;
98static constexpr std::uint64_t s3PutObjectVersionTagging = 1ULL << 52;
99static constexpr std::uint64_t s3DeleteObjectVersionTagging = 1ULL << 53;
100static constexpr std::uint64_t s3Count = 54;
31f18b77
FG
101static constexpr std::uint64_t s3All = (1ULL << s3Count) - 1;
102
103namespace {
104inline int op_to_perm(std::uint64_t op) {
105 switch (op) {
106 case s3GetObject:
107 case s3GetObjectTorrent:
108 case s3GetObjectVersion:
109 case s3GetObjectVersionTorrent:
224ce89b
WB
110 case s3GetObjectTagging:
111 case s3GetObjectVersionTagging:
31f18b77
FG
112 case s3ListAllMyBuckets:
113 case s3ListBucket:
114 case s3ListBucketMultiPartUploads:
115 case s3ListBucketVersions:
116 case s3ListMultipartUploadParts:
117 return RGW_PERM_READ;
118
119 case s3AbortMultipartUpload:
120 case s3CreateBucket:
121 case s3DeleteBucket:
122 case s3DeleteObject:
123 case s3DeleteObjectVersion:
124 case s3PutObject:
224ce89b
WB
125 case s3PutObjectTagging:
126 case s3PutObjectVersionTagging:
127 case s3DeleteObjectTagging:
128 case s3DeleteObjectVersionTagging:
31f18b77
FG
129 case s3RestoreObject:
130 return RGW_PERM_WRITE;
131
132 case s3GetAccelerateConfiguration:
133 case s3GetBucketAcl:
134 case s3GetBucketCORS:
135 case s3GetBucketLocation:
136 case s3GetBucketLogging:
137 case s3GetBucketNotification:
138 case s3GetBucketPolicy:
139 case s3GetBucketRequestPayment:
140 case s3GetBucketTagging:
141 case s3GetBucketVersioning:
142 case s3GetBucketWebsite:
143 case s3GetLifecycleConfiguration:
144 case s3GetObjectAcl:
145 case s3GetObjectVersionAcl:
146 case s3GetReplicationConfiguration:
147 return RGW_PERM_READ_ACP;
148
149 case s3DeleteBucketPolicy:
150 case s3DeleteBucketWebsite:
151 case s3DeleteReplicationConfiguration:
152 case s3PutAccelerateConfiguration:
153 case s3PutBucketAcl:
154 case s3PutBucketCORS:
155 case s3PutBucketLogging:
156 case s3PutBucketNotification:
157 case s3PutBucketPolicy:
158 case s3PutBucketRequestPayment:
159 case s3PutBucketTagging:
160 case s3PutBucketVersioning:
161 case s3PutBucketWebsite:
162 case s3PutLifecycleConfiguration:
163 case s3PutObjectAcl:
164 case s3PutObjectVersionAcl:
165 case s3PutReplicationConfiguration:
166 return RGW_PERM_WRITE_ACP;
167
168 case s3All:
169 return RGW_PERM_FULL_CONTROL;
170 }
171 return RGW_PERM_INVALID;
172}
173}
174
175using Environment = boost::container::flat_map<std::string, std::string>;
176
177enum struct Partition {
178 aws, aws_cn, aws_us_gov, wildcard
179 // If we wanted our own ARNs for principal type unique to us
180 // (maybe to integrate better with Swift) or for anything else we
181 // provide that doesn't map onto S3, we could add an 'rgw'
182 // partition type.
183};
184
185enum struct Service {
186 apigateway, appstream, artifact, autoscaling, aws_portal, acm,
187 cloudformation, cloudfront, cloudhsm, cloudsearch, cloudtrail,
188 cloudwatch, events, logs, codebuild, codecommit, codedeploy,
189 codepipeline, cognito_idp, cognito_identity, cognito_sync,
190 config, datapipeline, dms, devicefarm, directconnect,
191 ds, dynamodb, ec2, ecr, ecs, ssm, elasticbeanstalk, elasticfilesystem,
192 elasticloadbalancing, elasticmapreduce, elastictranscoder, elasticache,
193 es, gamelift, glacier, health, iam, importexport, inspector, iot,
194 kms, kinesisanalytics, firehose, kinesis, lambda, lightsail,
195 machinelearning, aws_marketplace, aws_marketplace_management,
196 mobileanalytics, mobilehub, opsworks, opsworks_cm, polly,
197 redshift, rds, route53, route53domains, sts, servicecatalog,
198 ses, sns, sqs, s3, swf, sdb, states, storagegateway, support,
199 trustedadvisor, waf, workmail, workspaces, wildcard
200};
201
202struct ARN {
203 Partition partition;
204 Service service;
205 std::string region;
206 // Once we refity tenant, we should probably use that instead of a
207 // string.
208 std::string account;
209 std::string resource;
210
211 ARN()
212 : partition(Partition::wildcard), service(Service::wildcard) {}
213 ARN(Partition partition, Service service, std::string region,
214 std::string account, std::string resource)
215 : partition(partition), service(service), region(std::move(region)),
216 account(std::move(account)), resource(std::move(resource)) {}
217 ARN(const rgw_obj& o);
218 ARN(const rgw_bucket& b);
219 ARN(const rgw_bucket& b, const std::string& o);
220
221 static boost::optional<ARN> parse(const std::string& s,
222 bool wildcard = false);
223 std::string to_string() const;
224
225 // `this` is the pattern
226 bool match(const ARN& candidate) const;
227};
228
229inline std::string to_string(const ARN& a) {
230 return a.to_string();
231}
232
233inline std::ostream& operator <<(std::ostream& m, const ARN& a) {
234 return m << to_string(a);
235}
236
237bool operator ==(const ARN& l, const ARN& r);
238bool operator <(const ARN& l, const ARN& r);
239
240using Address = std::bitset<128>;
241struct MaskedIP {
242 bool v6;
243 Address addr;
244 // Since we're mapping IPv6 to IPv4 addresses, we may want to
245 // consider making the prefix always be in terms of a v6 address
246 // and just use the v6 bit to rewrite it as a v4 prefix for
247 // output.
248 unsigned int prefix;
249};
250
251std::ostream& operator <<(std::ostream& m, const MaskedIP& ip);
252string to_string(const MaskedIP& m);
253
254inline bool operator ==(const MaskedIP& l, const MaskedIP& r) {
255 auto shift = std::max((l.v6 ? 128 : 32) - l.prefix,
256 (r.v6 ? 128 : 32) - r.prefix);
257 ceph_assert(shift > 0);
258 return (l.addr >> shift) == (r.addr >> shift);
259}
260
261struct Condition {
262 TokenID op;
263 // Originally I was going to use a perfect hash table, but Marcus
264 // says keys are to be added at run-time not compile time.
265
266 // In future development, use symbol internment.
267 std::string key;
268 bool ifexists = false;
269 // Much to my annoyance there is no actual way to do this in a
270 // typed way that is compatible with AWS. I know this because I've
271 // seen examples where the same value is used as a string in one
272 // context and a date in another.
273 std::vector<std::string> vals;
274
275 Condition() = default;
c07f9fc5
FG
276 Condition(TokenID op, const char* s, std::size_t len, bool ifexists)
277 : op(op), key(s, len), ifexists(ifexists) {}
31f18b77
FG
278
279 bool eval(const Environment& e) const;
280
281 static boost::optional<double> as_number(const std::string& s) {
282 std::size_t p = 0;
283
284 try {
285 double d = std::stod(s, &p);
286 if (p < s.length()) {
287 return boost::none;
288 }
289
290 return d;
291 } catch (const std::logic_error& e) {
292 return boost::none;
293 }
294 }
295
296 static boost::optional<ceph::real_time> as_date(const std::string& s) {
297 std::size_t p = 0;
298
299 try {
300 double d = std::stod(s, &p);
301 if (p == s.length()) {
302 return ceph::real_time(
303 std::chrono::seconds(static_cast<uint64_t>(d)) +
304 std::chrono::nanoseconds(
305 static_cast<uint64_t>((d - static_cast<uint64_t>(d))
306 * 1000000000)));
307 }
308
309 return from_iso_8601(boost::string_ref(s), false);
310 } catch (const std::logic_error& e) {
311 return boost::none;
312 }
313 }
314
315 static boost::optional<bool> as_bool(const std::string& s) {
316 std::size_t p = 0;
317
318 if (s.empty() || boost::iequals(s, "false")) {
319 return false;
320 }
321
322 try {
323 double d = std::stod(s, &p);
324 if (p == s.length()) {
c07f9fc5 325 return !((d == +0.0) || (d == -0.0) || std::isnan(d));
31f18b77
FG
326 }
327 } catch (const std::logic_error& e) {
328 // Fallthrough
329 }
330
331 return true;
332 }
333
334 static boost::optional<ceph::bufferlist> as_binary(const std::string& s) {
335 // In a just world
336 ceph::bufferlist base64;
337 // I could populate a bufferlist
338 base64.push_back(buffer::create_static(
339 s.length(),
340 const_cast<char*>(s.data()))); // Yuck
341 // From a base64 encoded std::string.
342 ceph::bufferlist bin;
343
344 try {
345 base64.decode_base64(bin);
346 } catch (const ceph::buffer::malformed_input& e) {
347 return boost::none;
348 }
349 return bin;
350 }
351
352 static boost::optional<MaskedIP> as_network(const std::string& s);
353
354
d2e6a577 355 struct ci_equal_to {
31f18b77
FG
356 bool operator ()(const std::string& s1,
357 const std::string& s2) const {
358 return boost::iequals(s1, s2);
359 }
360 };
361
d2e6a577
FG
362 struct string_like {
363 bool operator ()(const std::string& input,
364 const std::string& pattern) const {
365 return match_wildcards(pattern, input, 0);
366 }
367 };
31f18b77
FG
368
369 template<typename F>
370 static bool orrible(F&& f, const std::string& c,
371 const std::vector<std::string>& v) {
372 for (const auto& d : v) {
373 if (std::forward<F>(f)(c, d)) {
374 return true;
375 }
376 }
377 return false;
378 }
379
380 template<typename F, typename X>
381 static bool shortible(F&& f, X& x, const std::string& c,
382 const std::vector<std::string>& v) {
383 auto xc = std::forward<X>(x)(c);
384 if (!xc) {
385 return false;
386 }
387
388 for (const auto& d : v) {
389 auto xd = std::forward<X>(x)(d);
390 if (!xd) {
391 continue;
392 }
393
394 if (std::forward<F>(f)(*xc, *xd)) {
395 return true;
396 }
397 }
398 return false;
399 }
400};
401
402std::ostream& operator <<(std::ostream& m, const Condition& c);
403
404std::string to_string(const Condition& c);
405
406struct Statement {
407 boost::optional<std::string> sid = boost::none;
408
409 boost::container::flat_set<rgw::auth::Principal> princ;
410 boost::container::flat_set<rgw::auth::Principal> noprinc;
411
412 // Every statement MUST provide an effect. I just initialize it to
413 // deny as defensive programming.
414 Effect effect = Effect::Deny;
415
416 std::uint64_t action = 0;
417 std::uint64_t notaction = 0;
418
419 boost::container::flat_set<ARN> resource;
420 boost::container::flat_set<ARN> notresource;
421
422 std::vector<Condition> conditions;
423
424 Effect eval(const Environment& e,
425 boost::optional<const rgw::auth::Identity&> ida,
426 std::uint64_t action, const ARN& resource) const;
427};
428
429std::ostream& operator <<(ostream& m, const Statement& s);
430std::string to_string(const Statement& s);
431
432struct PolicyParseException : public std::exception {
433 rapidjson::ParseResult pr;
434
435 PolicyParseException(rapidjson::ParseResult&& pr)
436 : pr(pr) { }
437 const char* what() const noexcept override {
438 return rapidjson::GetParseError_En(pr.Code());
439 }
440};
441
442struct Policy {
443 std::string text;
444 Version version = Version::v2008_10_17;
445 boost::optional<std::string> id = boost::none;
446
447 std::vector<Statement> statements;
448
449 Policy(CephContext* cct, const std::string& tenant,
450 const bufferlist& text);
451
452 Effect eval(const Environment& e,
453 boost::optional<const rgw::auth::Identity&> ida,
454 std::uint64_t action, const ARN& resource) const;
455};
456
457std::ostream& operator <<(ostream& m, const Policy& p);
458std::string to_string(const Policy& p);
459}
460}
461
462namespace std {
463template<>
464struct hash<::rgw::IAM::Service> {
465 size_t operator()(const ::rgw::IAM::Service& s) const noexcept {
466 // Invoke a default-constructed hash object for int.
467 return hash<int>()(static_cast<int>(s));
468 }
469};
470}
471
472#endif