]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_iam_policy.h
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rgw / rgw_iam_policy.h
CommitLineData
31f18b77 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
31f18b77 3
1e59de90 4#pragma once
31f18b77
FG
5
6#include <bitset>
7#include <chrono>
8#include <cstdint>
9#include <iostream>
10#include <string>
f67539c2 11#include <string_view>
31f18b77
FG
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>
31f18b77
FG
18#include <boost/variant.hpp>
19
1e59de90
TL
20#include <fmt/format.h>
21
31f18b77
FG
22#include "common/ceph_time.h"
23#include "common/iso_8601.h"
24
25#include "rapidjson/error/error.h"
26#include "rapidjson/error/en.h"
27
c07f9fc5 28#include "rgw_acl.h"
31f18b77
FG
29#include "rgw_basic_types.h"
30#include "rgw_iam_policy_keywords.h"
d2e6a577 31#include "rgw_string.h"
eafe8130 32#include "rgw_arn.h"
31f18b77 33
31f18b77
FG
34namespace rgw {
35namespace auth {
36class Identity;
37}
38}
31f18b77
FG
39
40namespace rgw {
41namespace IAM {
11fdf7f2
TL
42
43static constexpr std::uint64_t s3GetObject = 0;
44static constexpr std::uint64_t s3GetObjectVersion = 1;
45static constexpr std::uint64_t s3PutObject = 2;
46static constexpr std::uint64_t s3GetObjectAcl = 3;
47static constexpr std::uint64_t s3GetObjectVersionAcl = 4;
48static constexpr std::uint64_t s3PutObjectAcl = 5;
49static constexpr std::uint64_t s3PutObjectVersionAcl = 6;
50static constexpr std::uint64_t s3DeleteObject = 7;
51static constexpr std::uint64_t s3DeleteObjectVersion = 8;
52static constexpr std::uint64_t s3ListMultipartUploadParts = 9;
53static constexpr std::uint64_t s3AbortMultipartUpload = 10;
54static constexpr std::uint64_t s3GetObjectTorrent = 11;
55static constexpr std::uint64_t s3GetObjectVersionTorrent = 12;
56static constexpr std::uint64_t s3RestoreObject = 13;
57static constexpr std::uint64_t s3CreateBucket = 14;
58static constexpr std::uint64_t s3DeleteBucket = 15;
59static constexpr std::uint64_t s3ListBucket = 16;
60static constexpr std::uint64_t s3ListBucketVersions = 17;
61static constexpr std::uint64_t s3ListAllMyBuckets = 18;
62static constexpr std::uint64_t s3ListBucketMultipartUploads = 19;
63static constexpr std::uint64_t s3GetAccelerateConfiguration = 20;
64static constexpr std::uint64_t s3PutAccelerateConfiguration = 21;
65static constexpr std::uint64_t s3GetBucketAcl = 22;
66static constexpr std::uint64_t s3PutBucketAcl = 23;
67static constexpr std::uint64_t s3GetBucketCORS = 24;
68static constexpr std::uint64_t s3PutBucketCORS = 25;
69static constexpr std::uint64_t s3GetBucketVersioning = 26;
70static constexpr std::uint64_t s3PutBucketVersioning = 27;
71static constexpr std::uint64_t s3GetBucketRequestPayment = 28;
72static constexpr std::uint64_t s3PutBucketRequestPayment = 29;
73static constexpr std::uint64_t s3GetBucketLocation = 30;
74static constexpr std::uint64_t s3GetBucketPolicy = 31;
75static constexpr std::uint64_t s3DeleteBucketPolicy = 32;
76static constexpr std::uint64_t s3PutBucketPolicy = 33;
77static constexpr std::uint64_t s3GetBucketNotification = 34;
78static constexpr std::uint64_t s3PutBucketNotification = 35;
79static constexpr std::uint64_t s3GetBucketLogging = 36;
80static constexpr std::uint64_t s3PutBucketLogging = 37;
81static constexpr std::uint64_t s3GetBucketTagging = 38;
82static constexpr std::uint64_t s3PutBucketTagging = 39;
83static constexpr std::uint64_t s3GetBucketWebsite = 40;
84static constexpr std::uint64_t s3PutBucketWebsite = 41;
85static constexpr std::uint64_t s3DeleteBucketWebsite = 42;
86static constexpr std::uint64_t s3GetLifecycleConfiguration = 43;
87static constexpr std::uint64_t s3PutLifecycleConfiguration = 44;
88static constexpr std::uint64_t s3PutReplicationConfiguration = 45;
89static constexpr std::uint64_t s3GetReplicationConfiguration = 46;
90static constexpr std::uint64_t s3DeleteReplicationConfiguration = 47;
91static constexpr std::uint64_t s3GetObjectTagging = 48;
92static constexpr std::uint64_t s3PutObjectTagging = 49;
93static constexpr std::uint64_t s3DeleteObjectTagging = 50;
94static constexpr std::uint64_t s3GetObjectVersionTagging = 51;
95static constexpr std::uint64_t s3PutObjectVersionTagging = 52;
96static constexpr std::uint64_t s3DeleteObjectVersionTagging = 53;
eafe8130
TL
97static constexpr std::uint64_t s3PutBucketObjectLockConfiguration = 54;
98static constexpr std::uint64_t s3GetBucketObjectLockConfiguration = 55;
99static constexpr std::uint64_t s3PutObjectRetention = 56;
100static constexpr std::uint64_t s3GetObjectRetention = 57;
101static constexpr std::uint64_t s3PutObjectLegalHold = 58;
102static constexpr std::uint64_t s3GetObjectLegalHold = 59;
103static constexpr std::uint64_t s3BypassGovernanceRetention = 60;
9f95a23c
TL
104static constexpr std::uint64_t s3GetBucketPolicyStatus = 61;
105static constexpr std::uint64_t s3PutPublicAccessBlock = 62;
106static constexpr std::uint64_t s3GetPublicAccessBlock = 63;
107static constexpr std::uint64_t s3DeletePublicAccessBlock = 64;
108static constexpr std::uint64_t s3GetBucketPublicAccessBlock = 65;
109static constexpr std::uint64_t s3PutBucketPublicAccessBlock = 66;
110static constexpr std::uint64_t s3DeleteBucketPublicAccessBlock = 67;
20effc67
TL
111static constexpr std::uint64_t s3GetBucketEncryption = 68;
112static constexpr std::uint64_t s3PutBucketEncryption = 69;
113static constexpr std::uint64_t s3All = 70;
9f95a23c
TL
114
115static constexpr std::uint64_t iamPutUserPolicy = s3All + 1;
116static constexpr std::uint64_t iamGetUserPolicy = s3All + 2;
117static constexpr std::uint64_t iamDeleteUserPolicy = s3All + 3;
118static constexpr std::uint64_t iamListUserPolicies = s3All + 4;
119static constexpr std::uint64_t iamCreateRole = s3All + 5;
120static constexpr std::uint64_t iamDeleteRole = s3All + 6;
1e59de90 121static constexpr std::uint64_t iamModifyRoleTrustPolicy = s3All + 7;
9f95a23c
TL
122static constexpr std::uint64_t iamGetRole = s3All + 8;
123static constexpr std::uint64_t iamListRoles = s3All + 9;
124static constexpr std::uint64_t iamPutRolePolicy = s3All + 10;
125static constexpr std::uint64_t iamGetRolePolicy = s3All + 11;
126static constexpr std::uint64_t iamListRolePolicies = s3All + 12;
127static constexpr std::uint64_t iamDeleteRolePolicy = s3All + 13;
f91f0fd5
TL
128static constexpr std::uint64_t iamCreateOIDCProvider = s3All + 14;
129static constexpr std::uint64_t iamDeleteOIDCProvider = s3All + 15;
130static constexpr std::uint64_t iamGetOIDCProvider = s3All + 16;
131static constexpr std::uint64_t iamListOIDCProviders = s3All + 17;
20effc67
TL
132static constexpr std::uint64_t iamTagRole = s3All + 18;
133static constexpr std::uint64_t iamListRoleTags = s3All + 19;
134static constexpr std::uint64_t iamUntagRole = s3All + 20;
1e59de90
TL
135static constexpr std::uint64_t iamUpdateRole = s3All + 21;
136static constexpr std::uint64_t iamAll = s3All + 22;
9f95a23c
TL
137
138static constexpr std::uint64_t stsAssumeRole = iamAll + 1;
139static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = iamAll + 2;
140static constexpr std::uint64_t stsGetSessionToken = iamAll + 3;
20effc67
TL
141static constexpr std::uint64_t stsTagSession = iamAll + 4;
142static constexpr std::uint64_t stsAll = iamAll + 5;
9f95a23c
TL
143
144static constexpr std::uint64_t s3Count = s3All;
11fdf7f2
TL
145static constexpr std::uint64_t allCount = stsAll + 1;
146
92f5a8d4 147using Action_t = std::bitset<allCount>;
11fdf7f2
TL
148using NotAction_t = Action_t;
149
9f95a23c
TL
150template <size_t N>
151constexpr std::bitset<N> make_bitmask(size_t s) {
152 // unfortunately none of the shift/logic operators of std::bitset have a constexpr variation
153 return s < 64 ? std::bitset<N> ((1ULL << s) - 1) :
154 std::bitset<N>((1ULL << 63) - 1) | make_bitmask<N> (s - 63) << 63;
155}
156
157template <size_t N>
158constexpr std::bitset<N> set_cont_bits(size_t start, size_t end)
159{
160 return (make_bitmask<N>(end - start)) << start;
161}
162
11fdf7f2 163static const Action_t None(0);
9f95a23c
TL
164static const Action_t s3AllValue = set_cont_bits<allCount>(0,s3All);
165static const Action_t iamAllValue = set_cont_bits<allCount>(s3All+1,iamAll);
166static const Action_t stsAllValue = set_cont_bits<allCount>(iamAll+1,stsAll);
167static const Action_t allValue = set_cont_bits<allCount>(0,allCount);
31f18b77
FG
168
169namespace {
a8e16298
TL
170// Please update the table in doc/radosgw/s3/authentication.rst if you
171// modify this function.
31f18b77
FG
172inline int op_to_perm(std::uint64_t op) {
173 switch (op) {
174 case s3GetObject:
175 case s3GetObjectTorrent:
176 case s3GetObjectVersion:
177 case s3GetObjectVersionTorrent:
224ce89b
WB
178 case s3GetObjectTagging:
179 case s3GetObjectVersionTagging:
eafe8130
TL
180 case s3GetObjectRetention:
181 case s3GetObjectLegalHold:
31f18b77
FG
182 case s3ListAllMyBuckets:
183 case s3ListBucket:
28e407b8 184 case s3ListBucketMultipartUploads:
31f18b77
FG
185 case s3ListBucketVersions:
186 case s3ListMultipartUploadParts:
187 return RGW_PERM_READ;
188
189 case s3AbortMultipartUpload:
190 case s3CreateBucket:
191 case s3DeleteBucket:
192 case s3DeleteObject:
193 case s3DeleteObjectVersion:
194 case s3PutObject:
224ce89b
WB
195 case s3PutObjectTagging:
196 case s3PutObjectVersionTagging:
197 case s3DeleteObjectTagging:
198 case s3DeleteObjectVersionTagging:
31f18b77 199 case s3RestoreObject:
eafe8130
TL
200 case s3PutObjectRetention:
201 case s3PutObjectLegalHold:
202 case s3BypassGovernanceRetention:
31f18b77
FG
203 return RGW_PERM_WRITE;
204
205 case s3GetAccelerateConfiguration:
206 case s3GetBucketAcl:
207 case s3GetBucketCORS:
20effc67 208 case s3GetBucketEncryption:
31f18b77
FG
209 case s3GetBucketLocation:
210 case s3GetBucketLogging:
211 case s3GetBucketNotification:
212 case s3GetBucketPolicy:
9f95a23c 213 case s3GetBucketPolicyStatus:
31f18b77
FG
214 case s3GetBucketRequestPayment:
215 case s3GetBucketTagging:
216 case s3GetBucketVersioning:
217 case s3GetBucketWebsite:
218 case s3GetLifecycleConfiguration:
219 case s3GetObjectAcl:
220 case s3GetObjectVersionAcl:
221 case s3GetReplicationConfiguration:
eafe8130 222 case s3GetBucketObjectLockConfiguration:
9f95a23c 223 case s3GetBucketPublicAccessBlock:
31f18b77
FG
224 return RGW_PERM_READ_ACP;
225
226 case s3DeleteBucketPolicy:
227 case s3DeleteBucketWebsite:
228 case s3DeleteReplicationConfiguration:
229 case s3PutAccelerateConfiguration:
230 case s3PutBucketAcl:
231 case s3PutBucketCORS:
20effc67 232 case s3PutBucketEncryption:
31f18b77
FG
233 case s3PutBucketLogging:
234 case s3PutBucketNotification:
235 case s3PutBucketPolicy:
236 case s3PutBucketRequestPayment:
237 case s3PutBucketTagging:
238 case s3PutBucketVersioning:
239 case s3PutBucketWebsite:
240 case s3PutLifecycleConfiguration:
241 case s3PutObjectAcl:
242 case s3PutObjectVersionAcl:
243 case s3PutReplicationConfiguration:
eafe8130 244 case s3PutBucketObjectLockConfiguration:
9f95a23c 245 case s3PutBucketPublicAccessBlock:
31f18b77
FG
246 return RGW_PERM_WRITE_ACP;
247
248 case s3All:
249 return RGW_PERM_FULL_CONTROL;
250 }
251 return RGW_PERM_INVALID;
252}
253}
254
522d829b
TL
255enum class PolicyPrincipal {
256 Role,
257 Session,
258 Other
259};
260
20effc67 261using Environment = std::unordered_multimap<std::string, std::string>;
31f18b77 262
31f18b77
FG
263using Address = std::bitset<128>;
264struct MaskedIP {
265 bool v6;
266 Address addr;
267 // Since we're mapping IPv6 to IPv4 addresses, we may want to
268 // consider making the prefix always be in terms of a v6 address
269 // and just use the v6 bit to rewrite it as a v4 prefix for
270 // output.
271 unsigned int prefix;
272};
273
274std::ostream& operator <<(std::ostream& m, const MaskedIP& ip);
31f18b77
FG
275
276inline bool operator ==(const MaskedIP& l, const MaskedIP& r) {
b32b8144
FG
277 auto shift = std::max((l.v6 ? 128 : 32) - ((int) l.prefix),
278 (r.v6 ? 128 : 32) - ((int) r.prefix));
279 ceph_assert(shift >= 0);
31f18b77
FG
280 return (l.addr >> shift) == (r.addr >> shift);
281}
282
283struct Condition {
284 TokenID op;
285 // Originally I was going to use a perfect hash table, but Marcus
286 // says keys are to be added at run-time not compile time.
287
288 // In future development, use symbol internment.
289 std::string key;
290 bool ifexists = false;
20effc67 291 bool isruntime = false; //Is evaluated during run-time
31f18b77
FG
292 // Much to my annoyance there is no actual way to do this in a
293 // typed way that is compatible with AWS. I know this because I've
294 // seen examples where the same value is used as a string in one
295 // context and a date in another.
296 std::vector<std::string> vals;
297
298 Condition() = default;
c07f9fc5
FG
299 Condition(TokenID op, const char* s, std::size_t len, bool ifexists)
300 : op(op), key(s, len), ifexists(ifexists) {}
31f18b77
FG
301
302 bool eval(const Environment& e) const;
303
304 static boost::optional<double> as_number(const std::string& s) {
305 std::size_t p = 0;
306
307 try {
308 double d = std::stod(s, &p);
309 if (p < s.length()) {
310 return boost::none;
311 }
312
313 return d;
314 } catch (const std::logic_error& e) {
315 return boost::none;
316 }
317 }
318
319 static boost::optional<ceph::real_time> as_date(const std::string& s) {
320 std::size_t p = 0;
321
322 try {
323 double d = std::stod(s, &p);
324 if (p == s.length()) {
325 return ceph::real_time(
326 std::chrono::seconds(static_cast<uint64_t>(d)) +
327 std::chrono::nanoseconds(
328 static_cast<uint64_t>((d - static_cast<uint64_t>(d))
329 * 1000000000)));
330 }
331
f67539c2 332 return from_iso_8601(std::string_view(s), false);
31f18b77
FG
333 } catch (const std::logic_error& e) {
334 return boost::none;
335 }
336 }
337
338 static boost::optional<bool> as_bool(const std::string& s) {
339 std::size_t p = 0;
340
341 if (s.empty() || boost::iequals(s, "false")) {
342 return false;
343 }
344
345 try {
346 double d = std::stod(s, &p);
347 if (p == s.length()) {
c07f9fc5 348 return !((d == +0.0) || (d == -0.0) || std::isnan(d));
31f18b77
FG
349 }
350 } catch (const std::logic_error& e) {
351 // Fallthrough
352 }
353
354 return true;
355 }
356
357 static boost::optional<ceph::bufferlist> as_binary(const std::string& s) {
358 // In a just world
359 ceph::bufferlist base64;
360 // I could populate a bufferlist
361 base64.push_back(buffer::create_static(
362 s.length(),
363 const_cast<char*>(s.data()))); // Yuck
364 // From a base64 encoded std::string.
365 ceph::bufferlist bin;
366
367 try {
92f5a8d4 368 bin.decode_base64(base64);
31f18b77
FG
369 } catch (const ceph::buffer::malformed_input& e) {
370 return boost::none;
371 }
372 return bin;
373 }
374
375 static boost::optional<MaskedIP> as_network(const std::string& s);
376
377
d2e6a577 378 struct ci_equal_to {
31f18b77
FG
379 bool operator ()(const std::string& s1,
380 const std::string& s2) const {
381 return boost::iequals(s1, s2);
382 }
383 };
384
d2e6a577
FG
385 struct string_like {
386 bool operator ()(const std::string& input,
387 const std::string& pattern) const {
388 return match_wildcards(pattern, input, 0);
389 }
390 };
31f18b77 391
11fdf7f2
TL
392 struct ci_starts_with {
393 bool operator()(const std::string& s1,
394 const std::string& s2) const {
395 return boost::istarts_with(s1, s2);
396 }
397 };
398
20effc67
TL
399 using unordered_multimap_it_pair = std::pair <std::unordered_multimap<std::string,std::string>::const_iterator, std::unordered_multimap<std::string,std::string>::const_iterator>;
400
31f18b77 401 template<typename F>
20effc67 402 static bool andible(F&& f, const unordered_multimap_it_pair& it,
31f18b77 403 const std::vector<std::string>& v) {
20effc67
TL
404 for (auto itr = it.first; itr != it.second; itr++) {
405 bool matched = false;
406 for (const auto& d : v) {
407 if (std::forward<F>(f)(itr->second, d)) {
408 matched = true;
31f18b77 409 }
20effc67
TL
410 }
411 if (!matched)
412 return false;
413 }
414 return true;
415 }
416
417 template<typename F>
418 static bool orrible(F&& f, const unordered_multimap_it_pair& it,
419 const std::vector<std::string>& v) {
420 for (auto itr = it.first; itr != it.second; itr++) {
421 for (const auto& d : v) {
422 if (std::forward<F>(f)(itr->second, d)) {
423 return true;
424 }
425 }
31f18b77
FG
426 }
427 return false;
428 }
429
430 template<typename F, typename X>
431 static bool shortible(F&& f, X& x, const std::string& c,
432 const std::vector<std::string>& v) {
433 auto xc = std::forward<X>(x)(c);
434 if (!xc) {
435 return false;
436 }
437
438 for (const auto& d : v) {
439 auto xd = std::forward<X>(x)(d);
440 if (!xd) {
441 continue;
442 }
443
444 if (std::forward<F>(f)(*xc, *xd)) {
445 return true;
446 }
447 }
448 return false;
449 }
11fdf7f2
TL
450
451 template <typename F>
452 bool has_key_p(const std::string& _key, F p) const {
453 return p(key, _key);
454 }
20effc67
TL
455
456 template <typename F>
457 bool has_val_p(const std::string& _val, F p) const {
458 for (auto val : vals) {
459 if (p(val, _val))
460 return true;
461 }
462 return false;
463 }
31f18b77
FG
464};
465
466std::ostream& operator <<(std::ostream& m, const Condition& c);
467
31f18b77
FG
468struct Statement {
469 boost::optional<std::string> sid = boost::none;
470
471 boost::container::flat_set<rgw::auth::Principal> princ;
472 boost::container::flat_set<rgw::auth::Principal> noprinc;
473
474 // Every statement MUST provide an effect. I just initialize it to
475 // deny as defensive programming.
476 Effect effect = Effect::Deny;
477
11fdf7f2
TL
478 Action_t action = 0;
479 NotAction_t notaction = 0;
31f18b77
FG
480
481 boost::container::flat_set<ARN> resource;
482 boost::container::flat_set<ARN> notresource;
483
484 std::vector<Condition> conditions;
485
486 Effect eval(const Environment& e,
487 boost::optional<const rgw::auth::Identity&> ida,
20effc67 488 std::uint64_t action, boost::optional<const ARN&> resource, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
11fdf7f2
TL
489
490 Effect eval_principal(const Environment& e,
522d829b 491 boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
11fdf7f2
TL
492
493 Effect eval_conditions(const Environment& e) const;
31f18b77
FG
494};
495
20effc67 496std::ostream& operator <<(std::ostream& m, const Statement& s);
31f18b77
FG
497
498struct PolicyParseException : public std::exception {
499 rapidjson::ParseResult pr;
1e59de90
TL
500 std::string msg;
501
502 explicit PolicyParseException(const rapidjson::ParseResult pr,
503 const std::string& annotation)
504 : pr(pr),
505 msg(fmt::format("At character offset {}, {}",
506 pr.Offset(),
507 (pr.Code() == rapidjson::kParseErrorTermination ?
508 annotation :
509 rapidjson::GetParseError_En(pr.Code())))) {}
31f18b77 510
31f18b77 511 const char* what() const noexcept override {
1e59de90 512 return msg.c_str();
31f18b77
FG
513 }
514};
515
516struct Policy {
517 std::string text;
518 Version version = Version::v2008_10_17;
519 boost::optional<std::string> id = boost::none;
520
521 std::vector<Statement> statements;
522
1e59de90
TL
523 // reject_invalid_principals should be set to
524 // `cct->_conf.get_val<bool>("rgw_policy_reject_invalid_principals")`
525 // when executing operations that *set* a bucket policy, but should
526 // be false when reading a stored bucket policy so as not to break
527 // backwards configuration.
31f18b77 528 Policy(CephContext* cct, const std::string& tenant,
1e59de90
TL
529 const bufferlist& text,
530 bool reject_invalid_principals);
31f18b77
FG
531
532 Effect eval(const Environment& e,
533 boost::optional<const rgw::auth::Identity&> ida,
20effc67 534 std::uint64_t action, boost::optional<const ARN&> resource, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
11fdf7f2
TL
535
536 Effect eval_principal(const Environment& e,
522d829b 537 boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
11fdf7f2
TL
538
539 Effect eval_conditions(const Environment& e) const;
540
541 template <typename F>
20effc67 542 bool has_conditional(const std::string& conditional, F p) const {
11fdf7f2
TL
543 for (const auto&s: statements){
544 if (std::any_of(s.conditions.begin(), s.conditions.end(),
545 [&](const Condition& c) { return c.has_key_p(conditional, p);}))
546 return true;
547 }
548 return false;
549 }
550
20effc67
TL
551 template <typename F>
552 bool has_conditional_value(const std::string& conditional, F p) const {
553 for (const auto&s: statements){
554 if (std::any_of(s.conditions.begin(), s.conditions.end(),
555 [&](const Condition& c) { return c.has_val_p(conditional, p);}))
556 return true;
557 }
558 return false;
559 }
560
561 bool has_conditional(const std::string& c) const {
11fdf7f2
TL
562 return has_conditional(c, Condition::ci_equal_to());
563 }
564
20effc67 565 bool has_partial_conditional(const std::string& c) const {
11fdf7f2
TL
566 return has_conditional(c, Condition::ci_starts_with());
567 }
20effc67
TL
568
569 // Example: ${s3:ResourceTag}
570 bool has_partial_conditional_value(const std::string& c) const {
571 return has_conditional_value(c, Condition::ci_starts_with());
572 }
31f18b77
FG
573};
574
20effc67 575std::ostream& operator <<(std::ostream& m, const Policy& p);
9f95a23c
TL
576bool is_public(const Policy& p);
577
31f18b77
FG
578}
579}