1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #ifndef CEPH_RGW_IAM_POLICY_H
5 #define CEPH_RGW_IAM_POLICY_H
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>
21 #include "common/ceph_time.h"
22 #include "common/iso_8601.h"
24 #include "rapidjson/error/error.h"
25 #include "rapidjson/error/en.h"
29 #include "rgw_basic_types.h"
30 #include "rgw_iam_policy_keywords.h"
32 #include "include/assert.h" // razzin' frazzin' ...grrr.
45 static constexpr std::uint64_t s3None
= 0;
46 static constexpr std::uint64_t s3GetObject
= 1ULL << 0;
47 static constexpr std::uint64_t s3GetObjectVersion
= 1ULL << 1;
48 static constexpr std::uint64_t s3PutObject
= 1ULL << 2;
49 static constexpr std::uint64_t s3GetObjectAcl
= 1ULL << 3;
50 static constexpr std::uint64_t s3GetObjectVersionAcl
= 1ULL << 4;
51 static constexpr std::uint64_t s3PutObjectAcl
= 1ULL << 5;
52 static constexpr std::uint64_t s3PutObjectVersionAcl
= 1ULL << 6;
53 static constexpr std::uint64_t s3DeleteObject
= 1ULL << 7;
54 static constexpr std::uint64_t s3DeleteObjectVersion
= 1ULL << 8;
55 static constexpr std::uint64_t s3ListMultipartUploadParts
= 1ULL << 9;
56 static constexpr std::uint64_t s3AbortMultipartUpload
= 1ULL << 10;
57 static constexpr std::uint64_t s3GetObjectTorrent
= 1ULL << 11;
58 static constexpr std::uint64_t s3GetObjectVersionTorrent
= 1ULL << 12;
59 static constexpr std::uint64_t s3RestoreObject
= 1ULL << 13;
60 static constexpr std::uint64_t s3CreateBucket
= 1ULL << 14;
61 static constexpr std::uint64_t s3DeleteBucket
= 1ULL << 15;
62 static constexpr std::uint64_t s3ListBucket
= 1ULL << 16;
63 static constexpr std::uint64_t s3ListBucketVersions
= 1ULL << 17;
64 static constexpr std::uint64_t s3ListAllMyBuckets
= 1ULL << 18;
65 static constexpr std::uint64_t s3ListBucketMultiPartUploads
= 1ULL << 19;
66 static constexpr std::uint64_t s3GetAccelerateConfiguration
= 1ULL << 20;
67 static constexpr std::uint64_t s3PutAccelerateConfiguration
= 1ULL << 21;
68 static constexpr std::uint64_t s3GetBucketAcl
= 1ULL << 22;
69 static constexpr std::uint64_t s3PutBucketAcl
= 1ULL << 23;
70 static constexpr std::uint64_t s3GetBucketCORS
= 1ULL << 24;
71 static constexpr std::uint64_t s3PutBucketCORS
= 1ULL << 25;
72 static constexpr std::uint64_t s3GetBucketVersioning
= 1ULL << 26;
73 static constexpr std::uint64_t s3PutBucketVersioning
= 1ULL << 27;
74 static constexpr std::uint64_t s3GetBucketRequestPayment
= 1ULL << 28;
75 static constexpr std::uint64_t s3PutBucketRequestPayment
= 1ULL << 29;
76 static constexpr std::uint64_t s3GetBucketLocation
= 1ULL << 30;
77 static constexpr std::uint64_t s3GetBucketPolicy
= 1ULL << 31;
78 static constexpr std::uint64_t s3DeleteBucketPolicy
= 1ULL << 32;
79 static constexpr std::uint64_t s3PutBucketPolicy
= 1ULL << 33;
80 static constexpr std::uint64_t s3GetBucketNotification
= 1ULL << 34;
81 static constexpr std::uint64_t s3PutBucketNotification
= 1ULL << 35;
82 static constexpr std::uint64_t s3GetBucketLogging
= 1ULL << 36;
83 static constexpr std::uint64_t s3PutBucketLogging
= 1ULL << 37;
84 static constexpr std::uint64_t s3GetBucketTagging
= 1ULL << 38;
85 static constexpr std::uint64_t s3PutBucketTagging
= 1ULL << 39;
86 static constexpr std::uint64_t s3GetBucketWebsite
= 1ULL << 40;
87 static constexpr std::uint64_t s3PutBucketWebsite
= 1ULL << 41;
88 static constexpr std::uint64_t s3DeleteBucketWebsite
= 1ULL << 42;
89 static constexpr std::uint64_t s3GetLifecycleConfiguration
= 1ULL << 43;
90 static constexpr std::uint64_t s3PutLifecycleConfiguration
= 1ULL << 44;
91 static constexpr std::uint64_t s3PutReplicationConfiguration
= 1ULL << 45;
92 static constexpr std::uint64_t s3GetReplicationConfiguration
= 1ULL << 46;
93 static constexpr std::uint64_t s3DeleteReplicationConfiguration
= 1ULL << 47;
94 static constexpr std::uint64_t s3Count
= 48;
95 static constexpr std::uint64_t s3All
= (1ULL << s3Count
) - 1;
98 inline int op_to_perm(std::uint64_t op
) {
101 case s3GetObjectTorrent
:
102 case s3GetObjectVersion
:
103 case s3GetObjectVersionTorrent
:
104 case s3ListAllMyBuckets
:
106 case s3ListBucketMultiPartUploads
:
107 case s3ListBucketVersions
:
108 case s3ListMultipartUploadParts
:
109 return RGW_PERM_READ
;
111 case s3AbortMultipartUpload
:
115 case s3DeleteObjectVersion
:
117 case s3RestoreObject
:
118 return RGW_PERM_WRITE
;
120 case s3GetAccelerateConfiguration
:
122 case s3GetBucketCORS
:
123 case s3GetBucketLocation
:
124 case s3GetBucketLogging
:
125 case s3GetBucketNotification
:
126 case s3GetBucketPolicy
:
127 case s3GetBucketRequestPayment
:
128 case s3GetBucketTagging
:
129 case s3GetBucketVersioning
:
130 case s3GetBucketWebsite
:
131 case s3GetLifecycleConfiguration
:
133 case s3GetObjectVersionAcl
:
134 case s3GetReplicationConfiguration
:
135 return RGW_PERM_READ_ACP
;
137 case s3DeleteBucketPolicy
:
138 case s3DeleteBucketWebsite
:
139 case s3DeleteReplicationConfiguration
:
140 case s3PutAccelerateConfiguration
:
142 case s3PutBucketCORS
:
143 case s3PutBucketLogging
:
144 case s3PutBucketNotification
:
145 case s3PutBucketPolicy
:
146 case s3PutBucketRequestPayment
:
147 case s3PutBucketTagging
:
148 case s3PutBucketVersioning
:
149 case s3PutBucketWebsite
:
150 case s3PutLifecycleConfiguration
:
152 case s3PutObjectVersionAcl
:
153 case s3PutReplicationConfiguration
:
154 return RGW_PERM_WRITE_ACP
;
157 return RGW_PERM_FULL_CONTROL
;
159 return RGW_PERM_INVALID
;
163 using Environment
= boost::container::flat_map
<std::string
, std::string
>;
165 enum struct Partition
{
166 aws
, aws_cn
, aws_us_gov
, wildcard
167 // If we wanted our own ARNs for principal type unique to us
168 // (maybe to integrate better with Swift) or for anything else we
169 // provide that doesn't map onto S3, we could add an 'rgw'
173 enum struct Service
{
174 apigateway
, appstream
, artifact
, autoscaling
, aws_portal
, acm
,
175 cloudformation
, cloudfront
, cloudhsm
, cloudsearch
, cloudtrail
,
176 cloudwatch
, events
, logs
, codebuild
, codecommit
, codedeploy
,
177 codepipeline
, cognito_idp
, cognito_identity
, cognito_sync
,
178 config
, datapipeline
, dms
, devicefarm
, directconnect
,
179 ds
, dynamodb
, ec2
, ecr
, ecs
, ssm
, elasticbeanstalk
, elasticfilesystem
,
180 elasticloadbalancing
, elasticmapreduce
, elastictranscoder
, elasticache
,
181 es
, gamelift
, glacier
, health
, iam
, importexport
, inspector
, iot
,
182 kms
, kinesisanalytics
, firehose
, kinesis
, lambda
, lightsail
,
183 machinelearning
, aws_marketplace
, aws_marketplace_management
,
184 mobileanalytics
, mobilehub
, opsworks
, opsworks_cm
, polly
,
185 redshift
, rds
, route53
, route53domains
, sts
, servicecatalog
,
186 ses
, sns
, sqs
, s3
, swf
, sdb
, states
, storagegateway
, support
,
187 trustedadvisor
, waf
, workmail
, workspaces
, wildcard
194 // Once we refity tenant, we should probably use that instead of a
197 std::string resource
;
200 : partition(Partition::wildcard
), service(Service::wildcard
) {}
201 ARN(Partition partition
, Service service
, std::string region
,
202 std::string account
, std::string resource
)
203 : partition(partition
), service(service
), region(std::move(region
)),
204 account(std::move(account
)), resource(std::move(resource
)) {}
205 ARN(const rgw_obj
& o
);
206 ARN(const rgw_bucket
& b
);
207 ARN(const rgw_bucket
& b
, const std::string
& o
);
209 static boost::optional
<ARN
> parse(const std::string
& s
,
210 bool wildcard
= false);
211 std::string
to_string() const;
213 // `this` is the pattern
214 bool match(const ARN
& candidate
) const;
217 inline std::string
to_string(const ARN
& a
) {
218 return a
.to_string();
221 inline std::ostream
& operator <<(std::ostream
& m
, const ARN
& a
) {
222 return m
<< to_string(a
);
225 bool operator ==(const ARN
& l
, const ARN
& r
);
226 bool operator <(const ARN
& l
, const ARN
& r
);
228 using Address
= std::bitset
<128>;
232 // Since we're mapping IPv6 to IPv4 addresses, we may want to
233 // consider making the prefix always be in terms of a v6 address
234 // and just use the v6 bit to rewrite it as a v4 prefix for
239 std::ostream
& operator <<(std::ostream
& m
, const MaskedIP
& ip
);
240 string
to_string(const MaskedIP
& m
);
242 inline bool operator ==(const MaskedIP
& l
, const MaskedIP
& r
) {
243 auto shift
= std::max((l
.v6
? 128 : 32) - l
.prefix
,
244 (r
.v6
? 128 : 32) - r
.prefix
);
245 ceph_assert(shift
> 0);
246 return (l
.addr
>> shift
) == (r
.addr
>> shift
);
251 // Originally I was going to use a perfect hash table, but Marcus
252 // says keys are to be added at run-time not compile time.
254 // In future development, use symbol internment.
256 bool ifexists
= false;
257 // Much to my annoyance there is no actual way to do this in a
258 // typed way that is compatible with AWS. I know this because I've
259 // seen examples where the same value is used as a string in one
260 // context and a date in another.
261 std::vector
<std::string
> vals
;
263 Condition() = default;
264 Condition(TokenID op
, const char* s
, std::size_t len
) : op(op
) {
265 static constexpr char ifexistr
[] = "IfExists";
266 auto l
= static_cast<const char*>(memmem(static_cast<const void*>(s
), len
,
267 static_cast<const void*>(ifexistr
),
268 sizeof(ifexistr
) -1));
269 if (l
&& ((l
+ sizeof(ifexistr
) - 1 == (s
+ len
)))) {
271 key
.assign(s
, static_cast<const char*>(l
) - s
);
277 bool eval(const Environment
& e
) const;
279 static boost::optional
<double> as_number(const std::string
& s
) {
283 double d
= std::stod(s
, &p
);
284 if (p
< s
.length()) {
289 } catch (const std::logic_error
& e
) {
294 static boost::optional
<ceph::real_time
> as_date(const std::string
& s
) {
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
))
307 return from_iso_8601(boost::string_ref(s
), false);
308 } catch (const std::logic_error
& e
) {
313 static boost::optional
<bool> as_bool(const std::string
& s
) {
316 if (s
.empty() || boost::iequals(s
, "false")) {
321 double d
= std::stod(s
, &p
);
322 if (p
== s
.length()) {
323 return !((d
== +0.0) || (d
= -0.0) || std::isnan(d
));
325 } catch (const std::logic_error
& e
) {
332 static boost::optional
<ceph::bufferlist
> as_binary(const std::string
& s
) {
334 ceph::bufferlist base64
;
335 // I could populate a bufferlist
336 base64
.push_back(buffer::create_static(
338 const_cast<char*>(s
.data()))); // Yuck
339 // From a base64 encoded std::string.
340 ceph::bufferlist bin
;
343 base64
.decode_base64(bin
);
344 } catch (const ceph::buffer::malformed_input
& e
) {
350 static boost::optional
<MaskedIP
> as_network(const std::string
& s
);
353 struct ci_equal_to
: public std::binary_function
<const std::string
,
356 bool operator ()(const std::string
& s1
,
357 const std::string
& s2
) const {
358 return boost::iequals(s1
, s2
);
364 static bool orrible(F
&& f
, const std::string
& c
,
365 const std::vector
<std::string
>& v
) {
366 for (const auto& d
: v
) {
367 if (std::forward
<F
>(f
)(c
, d
)) {
374 template<typename F
, typename X
>
375 static bool shortible(F
&& f
, X
& x
, const std::string
& c
,
376 const std::vector
<std::string
>& v
) {
377 auto xc
= std::forward
<X
>(x
)(c
);
382 for (const auto& d
: v
) {
383 auto xd
= std::forward
<X
>(x
)(d
);
388 if (std::forward
<F
>(f
)(*xc
, *xd
)) {
396 std::ostream
& operator <<(std::ostream
& m
, const Condition
& c
);
398 std::string
to_string(const Condition
& c
);
401 boost::optional
<std::string
> sid
= boost::none
;
403 boost::container::flat_set
<rgw::auth::Principal
> princ
;
404 boost::container::flat_set
<rgw::auth::Principal
> noprinc
;
406 // Every statement MUST provide an effect. I just initialize it to
407 // deny as defensive programming.
408 Effect effect
= Effect::Deny
;
410 std::uint64_t action
= 0;
411 std::uint64_t notaction
= 0;
413 boost::container::flat_set
<ARN
> resource
;
414 boost::container::flat_set
<ARN
> notresource
;
416 std::vector
<Condition
> conditions
;
418 Effect
eval(const Environment
& e
,
419 boost::optional
<const rgw::auth::Identity
&> ida
,
420 std::uint64_t action
, const ARN
& resource
) const;
423 std::ostream
& operator <<(ostream
& m
, const Statement
& s
);
424 std::string
to_string(const Statement
& s
);
426 struct PolicyParseException
: public std::exception
{
427 rapidjson::ParseResult pr
;
429 PolicyParseException(rapidjson::ParseResult
&& pr
)
431 const char* what() const noexcept override
{
432 return rapidjson::GetParseError_En(pr
.Code());
438 Version version
= Version::v2008_10_17
;
439 boost::optional
<std::string
> id
= boost::none
;
441 std::vector
<Statement
> statements
;
443 Policy(CephContext
* cct
, const std::string
& tenant
,
444 const bufferlist
& text
);
446 Effect
eval(const Environment
& e
,
447 boost::optional
<const rgw::auth::Identity
&> ida
,
448 std::uint64_t action
, const ARN
& resource
) const;
451 std::ostream
& operator <<(ostream
& m
, const Policy
& p
);
452 std::string
to_string(const Policy
& p
);
458 struct hash
<::rgw::IAM::Service
> {
459 size_t operator()(const ::rgw::IAM::Service
& s
) const noexcept
{
460 // Invoke a default-constructed hash object for int.
461 return hash
<int>()(static_cast<int>(s
));