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