]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_iam_policy.h
update sources to v12.1.1
[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
27#include "fnmatch.h"
28
29#include "rgw_basic_types.h"
30#include "rgw_iam_policy_keywords.h"
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;
276 Condition(TokenID op, const char* s, std::size_t len) : op(op) {
277 static constexpr char ifexistr[] = "IfExists";
278 auto l = static_cast<const char*>(memmem(static_cast<const void*>(s), len,
279 static_cast<const void*>(ifexistr),
280 sizeof(ifexistr) -1));
281 if (l && ((l + sizeof(ifexistr) - 1 == (s + len)))) {
282 ifexists = true;
283 key.assign(s, static_cast<const char*>(l) - s);
284 } else {
285 key.assign(s, len);
286 }
287 }
288
289 bool eval(const Environment& e) const;
290
291 static boost::optional<double> as_number(const std::string& s) {
292 std::size_t p = 0;
293
294 try {
295 double d = std::stod(s, &p);
296 if (p < s.length()) {
297 return boost::none;
298 }
299
300 return d;
301 } catch (const std::logic_error& e) {
302 return boost::none;
303 }
304 }
305
306 static boost::optional<ceph::real_time> as_date(const std::string& s) {
307 std::size_t p = 0;
308
309 try {
310 double d = std::stod(s, &p);
311 if (p == s.length()) {
312 return ceph::real_time(
313 std::chrono::seconds(static_cast<uint64_t>(d)) +
314 std::chrono::nanoseconds(
315 static_cast<uint64_t>((d - static_cast<uint64_t>(d))
316 * 1000000000)));
317 }
318
319 return from_iso_8601(boost::string_ref(s), false);
320 } catch (const std::logic_error& e) {
321 return boost::none;
322 }
323 }
324
325 static boost::optional<bool> as_bool(const std::string& s) {
326 std::size_t p = 0;
327
328 if (s.empty() || boost::iequals(s, "false")) {
329 return false;
330 }
331
332 try {
333 double d = std::stod(s, &p);
334 if (p == s.length()) {
335 return !((d == +0.0) || (d = -0.0) || std::isnan(d));
336 }
337 } catch (const std::logic_error& e) {
338 // Fallthrough
339 }
340
341 return true;
342 }
343
344 static boost::optional<ceph::bufferlist> as_binary(const std::string& s) {
345 // In a just world
346 ceph::bufferlist base64;
347 // I could populate a bufferlist
348 base64.push_back(buffer::create_static(
349 s.length(),
350 const_cast<char*>(s.data()))); // Yuck
351 // From a base64 encoded std::string.
352 ceph::bufferlist bin;
353
354 try {
355 base64.decode_base64(bin);
356 } catch (const ceph::buffer::malformed_input& e) {
357 return boost::none;
358 }
359 return bin;
360 }
361
362 static boost::optional<MaskedIP> as_network(const std::string& s);
363
364
365 struct ci_equal_to : public std::binary_function<const std::string,
366 const std::string,
367 bool> {
368 bool operator ()(const std::string& s1,
369 const std::string& s2) const {
370 return boost::iequals(s1, s2);
371 }
372 };
373
374
375 template<typename F>
376 static bool orrible(F&& f, const std::string& c,
377 const std::vector<std::string>& v) {
378 for (const auto& d : v) {
379 if (std::forward<F>(f)(c, d)) {
380 return true;
381 }
382 }
383 return false;
384 }
385
386 template<typename F, typename X>
387 static bool shortible(F&& f, X& x, const std::string& c,
388 const std::vector<std::string>& v) {
389 auto xc = std::forward<X>(x)(c);
390 if (!xc) {
391 return false;
392 }
393
394 for (const auto& d : v) {
395 auto xd = std::forward<X>(x)(d);
396 if (!xd) {
397 continue;
398 }
399
400 if (std::forward<F>(f)(*xc, *xd)) {
401 return true;
402 }
403 }
404 return false;
405 }
406};
407
408std::ostream& operator <<(std::ostream& m, const Condition& c);
409
410std::string to_string(const Condition& c);
411
412struct Statement {
413 boost::optional<std::string> sid = boost::none;
414
415 boost::container::flat_set<rgw::auth::Principal> princ;
416 boost::container::flat_set<rgw::auth::Principal> noprinc;
417
418 // Every statement MUST provide an effect. I just initialize it to
419 // deny as defensive programming.
420 Effect effect = Effect::Deny;
421
422 std::uint64_t action = 0;
423 std::uint64_t notaction = 0;
424
425 boost::container::flat_set<ARN> resource;
426 boost::container::flat_set<ARN> notresource;
427
428 std::vector<Condition> conditions;
429
430 Effect eval(const Environment& e,
431 boost::optional<const rgw::auth::Identity&> ida,
432 std::uint64_t action, const ARN& resource) const;
433};
434
435std::ostream& operator <<(ostream& m, const Statement& s);
436std::string to_string(const Statement& s);
437
438struct PolicyParseException : public std::exception {
439 rapidjson::ParseResult pr;
440
441 PolicyParseException(rapidjson::ParseResult&& pr)
442 : pr(pr) { }
443 const char* what() const noexcept override {
444 return rapidjson::GetParseError_En(pr.Code());
445 }
446};
447
448struct Policy {
449 std::string text;
450 Version version = Version::v2008_10_17;
451 boost::optional<std::string> id = boost::none;
452
453 std::vector<Statement> statements;
454
455 Policy(CephContext* cct, const std::string& tenant,
456 const bufferlist& text);
457
458 Effect eval(const Environment& e,
459 boost::optional<const rgw::auth::Identity&> ida,
460 std::uint64_t action, const ARN& resource) const;
461};
462
463std::ostream& operator <<(ostream& m, const Policy& p);
464std::string to_string(const Policy& p);
465}
466}
467
468namespace std {
469template<>
470struct hash<::rgw::IAM::Service> {
471 size_t operator()(const ::rgw::IAM::Service& s) const noexcept {
472 // Invoke a default-constructed hash object for int.
473 return hash<int>()(static_cast<int>(s));
474 }
475};
476}
477
478#endif