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"
28 #include "rgw_basic_types.h"
29 #include "rgw_iam_policy_keywords.h"
30 #include "rgw_string.h"
43 static constexpr std::uint64_t s3None
= 0;
44 static constexpr std::uint64_t s3GetObject
= 1ULL << 0;
45 static constexpr std::uint64_t s3GetObjectVersion
= 1ULL << 1;
46 static constexpr std::uint64_t s3PutObject
= 1ULL << 2;
47 static constexpr std::uint64_t s3GetObjectAcl
= 1ULL << 3;
48 static constexpr std::uint64_t s3GetObjectVersionAcl
= 1ULL << 4;
49 static constexpr std::uint64_t s3PutObjectAcl
= 1ULL << 5;
50 static constexpr std::uint64_t s3PutObjectVersionAcl
= 1ULL << 6;
51 static constexpr std::uint64_t s3DeleteObject
= 1ULL << 7;
52 static constexpr std::uint64_t s3DeleteObjectVersion
= 1ULL << 8;
53 static constexpr std::uint64_t s3ListMultipartUploadParts
= 1ULL << 9;
54 static constexpr std::uint64_t s3AbortMultipartUpload
= 1ULL << 10;
55 static constexpr std::uint64_t s3GetObjectTorrent
= 1ULL << 11;
56 static constexpr std::uint64_t s3GetObjectVersionTorrent
= 1ULL << 12;
57 static constexpr std::uint64_t s3RestoreObject
= 1ULL << 13;
58 static constexpr std::uint64_t s3CreateBucket
= 1ULL << 14;
59 static constexpr std::uint64_t s3DeleteBucket
= 1ULL << 15;
60 static constexpr std::uint64_t s3ListBucket
= 1ULL << 16;
61 static constexpr std::uint64_t s3ListBucketVersions
= 1ULL << 17;
62 static constexpr std::uint64_t s3ListAllMyBuckets
= 1ULL << 18;
63 static constexpr std::uint64_t s3ListBucketMultiPartUploads
= 1ULL << 19;
64 static constexpr std::uint64_t s3GetAccelerateConfiguration
= 1ULL << 20;
65 static constexpr std::uint64_t s3PutAccelerateConfiguration
= 1ULL << 21;
66 static constexpr std::uint64_t s3GetBucketAcl
= 1ULL << 22;
67 static constexpr std::uint64_t s3PutBucketAcl
= 1ULL << 23;
68 static constexpr std::uint64_t s3GetBucketCORS
= 1ULL << 24;
69 static constexpr std::uint64_t s3PutBucketCORS
= 1ULL << 25;
70 static constexpr std::uint64_t s3GetBucketVersioning
= 1ULL << 26;
71 static constexpr std::uint64_t s3PutBucketVersioning
= 1ULL << 27;
72 static constexpr std::uint64_t s3GetBucketRequestPayment
= 1ULL << 28;
73 static constexpr std::uint64_t s3PutBucketRequestPayment
= 1ULL << 29;
74 static constexpr std::uint64_t s3GetBucketLocation
= 1ULL << 30;
75 static constexpr std::uint64_t s3GetBucketPolicy
= 1ULL << 31;
76 static constexpr std::uint64_t s3DeleteBucketPolicy
= 1ULL << 32;
77 static constexpr std::uint64_t s3PutBucketPolicy
= 1ULL << 33;
78 static constexpr std::uint64_t s3GetBucketNotification
= 1ULL << 34;
79 static constexpr std::uint64_t s3PutBucketNotification
= 1ULL << 35;
80 static constexpr std::uint64_t s3GetBucketLogging
= 1ULL << 36;
81 static constexpr std::uint64_t s3PutBucketLogging
= 1ULL << 37;
82 static constexpr std::uint64_t s3GetBucketTagging
= 1ULL << 38;
83 static constexpr std::uint64_t s3PutBucketTagging
= 1ULL << 39;
84 static constexpr std::uint64_t s3GetBucketWebsite
= 1ULL << 40;
85 static constexpr std::uint64_t s3PutBucketWebsite
= 1ULL << 41;
86 static constexpr std::uint64_t s3DeleteBucketWebsite
= 1ULL << 42;
87 static constexpr std::uint64_t s3GetLifecycleConfiguration
= 1ULL << 43;
88 static constexpr std::uint64_t s3PutLifecycleConfiguration
= 1ULL << 44;
89 static constexpr std::uint64_t s3PutReplicationConfiguration
= 1ULL << 45;
90 static constexpr std::uint64_t s3GetReplicationConfiguration
= 1ULL << 46;
91 static constexpr std::uint64_t s3DeleteReplicationConfiguration
= 1ULL << 47;
92 static constexpr std::uint64_t s3GetObjectTagging
= 1ULL << 48;
93 static constexpr std::uint64_t s3PutObjectTagging
= 1ULL << 49;
94 static constexpr std::uint64_t s3DeleteObjectTagging
= 1ULL << 50;
95 static constexpr std::uint64_t s3GetObjectVersionTagging
= 1ULL << 51;
96 static constexpr std::uint64_t s3PutObjectVersionTagging
= 1ULL << 52;
97 static constexpr std::uint64_t s3DeleteObjectVersionTagging
= 1ULL << 53;
98 static constexpr std::uint64_t s3Count
= 54;
99 static constexpr std::uint64_t s3All
= (1ULL << s3Count
) - 1;
102 inline int op_to_perm(std::uint64_t op
) {
105 case s3GetObjectTorrent
:
106 case s3GetObjectVersion
:
107 case s3GetObjectVersionTorrent
:
108 case s3GetObjectTagging
:
109 case s3GetObjectVersionTagging
:
110 case s3ListAllMyBuckets
:
112 case s3ListBucketMultiPartUploads
:
113 case s3ListBucketVersions
:
114 case s3ListMultipartUploadParts
:
115 return RGW_PERM_READ
;
117 case s3AbortMultipartUpload
:
121 case s3DeleteObjectVersion
:
123 case s3PutObjectTagging
:
124 case s3PutObjectVersionTagging
:
125 case s3DeleteObjectTagging
:
126 case s3DeleteObjectVersionTagging
:
127 case s3RestoreObject
:
128 return RGW_PERM_WRITE
;
130 case s3GetAccelerateConfiguration
:
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
:
143 case s3GetObjectVersionAcl
:
144 case s3GetReplicationConfiguration
:
145 return RGW_PERM_READ_ACP
;
147 case s3DeleteBucketPolicy
:
148 case s3DeleteBucketWebsite
:
149 case s3DeleteReplicationConfiguration
:
150 case s3PutAccelerateConfiguration
:
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
:
162 case s3PutObjectVersionAcl
:
163 case s3PutReplicationConfiguration
:
164 return RGW_PERM_WRITE_ACP
;
167 return RGW_PERM_FULL_CONTROL
;
169 return RGW_PERM_INVALID
;
173 using Environment
= boost::container::flat_map
<std::string
, std::string
>;
175 enum 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'
183 enum 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
204 // Once we refity tenant, we should probably use that instead of a
207 std::string resource
;
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
);
219 static boost::optional
<ARN
> parse(const std::string
& s
,
220 bool wildcard
= false);
221 std::string
to_string() const;
223 // `this` is the pattern
224 bool match(const ARN
& candidate
) const;
227 inline std::string
to_string(const ARN
& a
) {
228 return a
.to_string();
231 inline std::ostream
& operator <<(std::ostream
& m
, const ARN
& a
) {
232 return m
<< to_string(a
);
235 bool operator ==(const ARN
& l
, const ARN
& r
);
236 bool operator <(const ARN
& l
, const ARN
& r
);
238 using Address
= std::bitset
<128>;
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
249 std::ostream
& operator <<(std::ostream
& m
, const MaskedIP
& ip
);
250 string
to_string(const MaskedIP
& m
);
252 inline bool operator ==(const MaskedIP
& l
, const MaskedIP
& r
) {
253 auto shift
= std::max((l
.v6
? 128 : 32) - l
.prefix
,
254 (r
.v6
? 128 : 32) - r
.prefix
);
255 return (l
.addr
>> shift
) == (r
.addr
>> shift
);
260 // Originally I was going to use a perfect hash table, but Marcus
261 // says keys are to be added at run-time not compile time.
263 // In future development, use symbol internment.
265 bool ifexists
= false;
266 // Much to my annoyance there is no actual way to do this in a
267 // typed way that is compatible with AWS. I know this because I've
268 // seen examples where the same value is used as a string in one
269 // context and a date in another.
270 std::vector
<std::string
> vals
;
272 Condition() = default;
273 Condition(TokenID op
, const char* s
, std::size_t len
, bool ifexists
)
274 : op(op
), key(s
, len
), ifexists(ifexists
) {}
276 bool eval(const Environment
& e
) const;
278 static boost::optional
<double> as_number(const std::string
& s
) {
282 double d
= std::stod(s
, &p
);
283 if (p
< s
.length()) {
288 } catch (const std::logic_error
& e
) {
293 static boost::optional
<ceph::real_time
> as_date(const std::string
& s
) {
297 double d
= std::stod(s
, &p
);
298 if (p
== s
.length()) {
299 return ceph::real_time(
300 std::chrono::seconds(static_cast<uint64_t>(d
)) +
301 std::chrono::nanoseconds(
302 static_cast<uint64_t>((d
- static_cast<uint64_t>(d
))
306 return from_iso_8601(boost::string_ref(s
), false);
307 } catch (const std::logic_error
& e
) {
312 static boost::optional
<bool> as_bool(const std::string
& s
) {
315 if (s
.empty() || boost::iequals(s
, "false")) {
320 double d
= std::stod(s
, &p
);
321 if (p
== s
.length()) {
322 return !((d
== +0.0) || (d
== -0.0) || std::isnan(d
));
324 } catch (const std::logic_error
& e
) {
331 static boost::optional
<ceph::bufferlist
> as_binary(const std::string
& s
) {
333 ceph::bufferlist base64
;
334 // I could populate a bufferlist
335 base64
.push_back(buffer::create_static(
337 const_cast<char*>(s
.data()))); // Yuck
338 // From a base64 encoded std::string.
339 ceph::bufferlist bin
;
342 base64
.decode_base64(bin
);
343 } catch (const ceph::buffer::malformed_input
& e
) {
349 static boost::optional
<MaskedIP
> as_network(const std::string
& s
);
353 bool operator ()(const std::string
& s1
,
354 const std::string
& s2
) const {
355 return boost::iequals(s1
, s2
);
360 bool operator ()(const std::string
& input
,
361 const std::string
& pattern
) const {
362 return match_wildcards(pattern
, input
, 0);
367 static bool orrible(F
&& f
, const std::string
& c
,
368 const std::vector
<std::string
>& v
) {
369 for (const auto& d
: v
) {
370 if (std::forward
<F
>(f
)(c
, d
)) {
377 template<typename F
, typename X
>
378 static bool shortible(F
&& f
, X
& x
, const std::string
& c
,
379 const std::vector
<std::string
>& v
) {
380 auto xc
= std::forward
<X
>(x
)(c
);
385 for (const auto& d
: v
) {
386 auto xd
= std::forward
<X
>(x
)(d
);
391 if (std::forward
<F
>(f
)(*xc
, *xd
)) {
399 std::ostream
& operator <<(std::ostream
& m
, const Condition
& c
);
401 std::string
to_string(const Condition
& c
);
404 boost::optional
<std::string
> sid
= boost::none
;
406 boost::container::flat_set
<rgw::auth::Principal
> princ
;
407 boost::container::flat_set
<rgw::auth::Principal
> noprinc
;
409 // Every statement MUST provide an effect. I just initialize it to
410 // deny as defensive programming.
411 Effect effect
= Effect::Deny
;
413 std::uint64_t action
= 0;
414 std::uint64_t notaction
= 0;
416 boost::container::flat_set
<ARN
> resource
;
417 boost::container::flat_set
<ARN
> notresource
;
419 std::vector
<Condition
> conditions
;
421 Effect
eval(const Environment
& e
,
422 boost::optional
<const rgw::auth::Identity
&> ida
,
423 std::uint64_t action
, const ARN
& resource
) const;
426 std::ostream
& operator <<(ostream
& m
, const Statement
& s
);
427 std::string
to_string(const Statement
& s
);
429 struct PolicyParseException
: public std::exception
{
430 rapidjson::ParseResult pr
;
432 PolicyParseException(rapidjson::ParseResult
&& pr
)
434 const char* what() const noexcept override
{
435 return rapidjson::GetParseError_En(pr
.Code());
441 Version version
= Version::v2008_10_17
;
442 boost::optional
<std::string
> id
= boost::none
;
444 std::vector
<Statement
> statements
;
446 Policy(CephContext
* cct
, const std::string
& tenant
,
447 const bufferlist
& text
);
449 Effect
eval(const Environment
& e
,
450 boost::optional
<const rgw::auth::Identity
&> ida
,
451 std::uint64_t action
, const ARN
& resource
) const;
454 std::ostream
& operator <<(ostream
& m
, const Policy
& p
);
455 std::string
to_string(const Policy
& p
);
461 struct hash
<::rgw::IAM::Service
> {
462 size_t operator()(const ::rgw::IAM::Service
& s
) const noexcept
{
463 // Invoke a default-constructed hash object for int.
464 return hash
<int>()(static_cast<int>(s
));