1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2015 Red Hat
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
17 #include <boost/intrusive_ptr.hpp>
18 #include <boost/optional.hpp>
20 #include <gtest/gtest.h>
22 #include "common/code_environment.h"
23 #include "common/ceph_context.h"
24 #include "global/global_init.h"
25 #include "rgw/rgw_auth.h"
26 #include "rgw/rgw_iam_policy.h"
32 using boost::container::flat_set
;
33 using boost::intrusive_ptr
;
34 using boost::make_optional
;
36 using boost::optional
;
38 using rgw::auth::Identity
;
39 using rgw::auth::Principal
;
42 using rgw::IAM::Effect
;
43 using rgw::IAM::Environment
;
44 using rgw::IAM::Partition
;
45 using rgw::IAM::Policy
;
46 using rgw::IAM::s3All
;
47 using rgw::IAM::s3Count
;
48 using rgw::IAM::s3GetAccelerateConfiguration
;
49 using rgw::IAM::s3GetBucketAcl
;
50 using rgw::IAM::s3GetBucketCORS
;
51 using rgw::IAM::s3GetBucketLocation
;
52 using rgw::IAM::s3GetBucketLogging
;
53 using rgw::IAM::s3GetBucketNotification
;
54 using rgw::IAM::s3GetBucketPolicy
;
55 using rgw::IAM::s3GetBucketRequestPayment
;
56 using rgw::IAM::s3GetBucketTagging
;
57 using rgw::IAM::s3GetBucketVersioning
;
58 using rgw::IAM::s3GetBucketWebsite
;
59 using rgw::IAM::s3GetLifecycleConfiguration
;
60 using rgw::IAM::s3GetObject
;
61 using rgw::IAM::s3GetObjectAcl
;
62 using rgw::IAM::s3GetObjectVersionAcl
;
63 using rgw::IAM::s3GetObjectTorrent
;
64 using rgw::IAM::s3GetObjectVersion
;
65 using rgw::IAM::s3GetObjectVersionTorrent
;
66 using rgw::IAM::s3GetReplicationConfiguration
;
67 using rgw::IAM::s3ListAllMyBuckets
;
68 using rgw::IAM::s3ListBucket
;
69 using rgw::IAM::s3ListBucket
;
70 using rgw::IAM::s3ListBucketMultiPartUploads
;
71 using rgw::IAM::s3ListBucketVersions
;
72 using rgw::IAM::s3ListMultipartUploadParts
;
73 using rgw::IAM::s3None
;
74 using rgw::IAM::s3PutBucketAcl
;
75 using rgw::IAM::s3PutBucketPolicy
;
76 using rgw::IAM::Service
;
77 using rgw::IAM::TokenID
;
78 using rgw::IAM::Version
;
80 class FakeIdentity
: public Identity
{
84 FakeIdentity(Principal
&& id
) : id(std::move(id
)) {}
85 uint32_t get_perms_from_aclspec(const aclspec_t
& aclspec
) const override
{
90 bool is_admin_of(const rgw_user
& uid
) const override
{
95 bool is_owner_of(const rgw_user
& uid
) const override
{
100 virtual uint32_t get_perm_mask() const override
{
105 void to_str(std::ostream
& out
) const override
{
109 bool is_identity(const flat_set
<Principal
>& ids
) const override
{
110 return ids
.find(id
) != ids
.end();
114 class PolicyTest
: public ::testing::Test
{
116 intrusive_ptr
<CephContext
> cct
;
117 static const string arbitrary_tenant
;
118 static string example1
;
119 static string example2
;
120 static string example3
;
123 cct
= new CephContext(CEPH_ENTITY_TYPE_CLIENT
);
127 TEST_F(PolicyTest
, Parse1
) {
130 ASSERT_NO_THROW(p
= Policy(cct
.get(), arbitrary_tenant
,
131 bufferlist::static_from_string(example1
)));
134 EXPECT_EQ(p
->text
, example1
);
135 EXPECT_EQ(p
->version
, Version::v2012_10_17
);
137 EXPECT_FALSE(p
->statements
[0].sid
);
138 EXPECT_FALSE(p
->statements
.empty());
139 EXPECT_EQ(p
->statements
.size(), 1U);
140 EXPECT_TRUE(p
->statements
[0].princ
.empty());
141 EXPECT_TRUE(p
->statements
[0].noprinc
.empty());
142 EXPECT_EQ(p
->statements
[0].effect
, Effect::Allow
);
143 EXPECT_EQ(p
->statements
[0].action
, s3ListBucket
);
144 EXPECT_EQ(p
->statements
[0].notaction
, s3None
);
145 ASSERT_FALSE(p
->statements
[0].resource
.empty());
146 ASSERT_EQ(p
->statements
[0].resource
.size(), 1U);
147 EXPECT_EQ(p
->statements
[0].resource
.begin()->partition
, Partition::aws
);
148 EXPECT_EQ(p
->statements
[0].resource
.begin()->service
, Service::s3
);
149 EXPECT_TRUE(p
->statements
[0].resource
.begin()->region
.empty());
150 EXPECT_EQ(p
->statements
[0].resource
.begin()->account
, arbitrary_tenant
);
151 EXPECT_EQ(p
->statements
[0].resource
.begin()->resource
, "example_bucket");
152 EXPECT_TRUE(p
->statements
[0].notresource
.empty());
153 EXPECT_TRUE(p
->statements
[0].conditions
.empty());
156 TEST_F(PolicyTest
, Eval1
) {
157 auto p
= Policy(cct
.get(), arbitrary_tenant
,
158 bufferlist::static_from_string(example1
));
161 EXPECT_EQ(p
.eval(e
, none
, s3ListBucket
,
162 ARN(Partition::aws
, Service::s3
,
163 "", arbitrary_tenant
, "example_bucket")),
166 EXPECT_EQ(p
.eval(e
, none
, s3PutBucketAcl
,
167 ARN(Partition::aws
, Service::s3
,
168 "", arbitrary_tenant
, "example_bucket")),
171 EXPECT_EQ(p
.eval(e
, none
, s3ListBucket
,
172 ARN(Partition::aws
, Service::s3
,
173 "", arbitrary_tenant
, "erroneous_bucket")),
178 TEST_F(PolicyTest
, Parse2
) {
181 ASSERT_NO_THROW(p
= Policy(cct
.get(), arbitrary_tenant
,
182 bufferlist::static_from_string(example2
)));
185 EXPECT_EQ(p
->text
, example2
);
186 EXPECT_EQ(p
->version
, Version::v2012_10_17
);
187 EXPECT_EQ(*p
->id
, "S3-Account-Permissions");
188 ASSERT_FALSE(p
->statements
.empty());
189 EXPECT_EQ(p
->statements
.size(), 1U);
190 EXPECT_EQ(*p
->statements
[0].sid
, "1");
191 EXPECT_FALSE(p
->statements
[0].princ
.empty());
192 EXPECT_EQ(p
->statements
[0].princ
.size(), 1U);
193 EXPECT_EQ(*p
->statements
[0].princ
.begin(),
194 Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
195 EXPECT_TRUE(p
->statements
[0].noprinc
.empty());
196 EXPECT_EQ(p
->statements
[0].effect
, Effect::Allow
);
197 EXPECT_EQ(p
->statements
[0].action
, s3All
);
198 EXPECT_EQ(p
->statements
[0].notaction
, s3None
);
199 ASSERT_FALSE(p
->statements
[0].resource
.empty());
200 ASSERT_EQ(p
->statements
[0].resource
.size(), 2U);
201 EXPECT_EQ(p
->statements
[0].resource
.begin()->partition
, Partition::aws
);
202 EXPECT_EQ(p
->statements
[0].resource
.begin()->service
, Service::s3
);
203 EXPECT_TRUE(p
->statements
[0].resource
.begin()->region
.empty());
204 EXPECT_EQ(p
->statements
[0].resource
.begin()->account
, arbitrary_tenant
);
205 EXPECT_EQ(p
->statements
[0].resource
.begin()->resource
, "mybucket");
206 EXPECT_EQ((p
->statements
[0].resource
.begin() + 1)->partition
,
208 EXPECT_EQ((p
->statements
[0].resource
.begin() + 1)->service
,
210 EXPECT_TRUE((p
->statements
[0].resource
.begin() + 1)->region
.empty());
211 EXPECT_EQ((p
->statements
[0].resource
.begin() + 1)->account
,
213 EXPECT_EQ((p
->statements
[0].resource
.begin() + 1)->resource
, "mybucket/*");
214 EXPECT_TRUE(p
->statements
[0].notresource
.empty());
215 EXPECT_TRUE(p
->statements
[0].conditions
.empty());
218 TEST_F(PolicyTest
, Eval2
) {
219 auto p
= Policy(cct
.get(), arbitrary_tenant
,
220 bufferlist::static_from_string(example2
));
223 auto trueacct
= FakeIdentity(
224 Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
226 auto notacct
= FakeIdentity(
227 Principal::tenant("some-other-account"));
228 for (auto i
= 0ULL; i
< s3Count
; ++i
) {
229 EXPECT_EQ(p
.eval(e
, trueacct
, 1ULL << i
,
230 ARN(Partition::aws
, Service::s3
,
231 "", arbitrary_tenant
, "mybucket")),
233 EXPECT_EQ(p
.eval(e
, trueacct
, 1ULL << i
,
234 ARN(Partition::aws
, Service::s3
,
235 "", arbitrary_tenant
, "mybucket/myobject")),
238 EXPECT_EQ(p
.eval(e
, notacct
, 1ULL << i
,
239 ARN(Partition::aws
, Service::s3
,
240 "", arbitrary_tenant
, "mybucket")),
242 EXPECT_EQ(p
.eval(e
, notacct
, 1ULL << i
,
243 ARN(Partition::aws
, Service::s3
,
244 "", arbitrary_tenant
, "mybucket/myobject")),
247 EXPECT_EQ(p
.eval(e
, trueacct
, 1ULL << i
,
248 ARN(Partition::aws
, Service::s3
,
249 "", arbitrary_tenant
, "notyourbucket")),
251 EXPECT_EQ(p
.eval(e
, trueacct
, 1ULL << i
,
252 ARN(Partition::aws
, Service::s3
,
253 "", arbitrary_tenant
, "notyourbucket/notyourobject")),
259 TEST_F(PolicyTest
, Parse3
) {
262 ASSERT_NO_THROW(p
= Policy(cct
.get(), arbitrary_tenant
,
263 bufferlist::static_from_string(example3
)));
266 EXPECT_EQ(p
->text
, example3
);
267 EXPECT_EQ(p
->version
, Version::v2012_10_17
);
269 ASSERT_FALSE(p
->statements
.empty());
270 EXPECT_EQ(p
->statements
.size(), 3U);
272 EXPECT_EQ(*p
->statements
[0].sid
, "FirstStatement");
273 EXPECT_TRUE(p
->statements
[0].princ
.empty());
274 EXPECT_TRUE(p
->statements
[0].noprinc
.empty());
275 EXPECT_EQ(p
->statements
[0].effect
, Effect::Allow
);
276 EXPECT_EQ(p
->statements
[0].action
, s3PutBucketPolicy
);
277 EXPECT_EQ(p
->statements
[0].notaction
, s3None
);
278 ASSERT_FALSE(p
->statements
[0].resource
.empty());
279 ASSERT_EQ(p
->statements
[0].resource
.size(), 1U);
280 EXPECT_EQ(p
->statements
[0].resource
.begin()->partition
, Partition::wildcard
);
281 EXPECT_EQ(p
->statements
[0].resource
.begin()->service
, Service::wildcard
);
282 EXPECT_EQ(p
->statements
[0].resource
.begin()->region
, "*");
283 EXPECT_EQ(p
->statements
[0].resource
.begin()->account
, arbitrary_tenant
);
284 EXPECT_EQ(p
->statements
[0].resource
.begin()->resource
, "*");
285 EXPECT_TRUE(p
->statements
[0].notresource
.empty());
286 EXPECT_TRUE(p
->statements
[0].conditions
.empty());
288 EXPECT_EQ(*p
->statements
[1].sid
, "SecondStatement");
289 EXPECT_TRUE(p
->statements
[1].princ
.empty());
290 EXPECT_TRUE(p
->statements
[1].noprinc
.empty());
291 EXPECT_EQ(p
->statements
[1].effect
, Effect::Allow
);
292 EXPECT_EQ(p
->statements
[1].action
, s3ListAllMyBuckets
);
293 EXPECT_EQ(p
->statements
[1].notaction
, s3None
);
294 ASSERT_FALSE(p
->statements
[1].resource
.empty());
295 ASSERT_EQ(p
->statements
[1].resource
.size(), 1U);
296 EXPECT_EQ(p
->statements
[1].resource
.begin()->partition
, Partition::wildcard
);
297 EXPECT_EQ(p
->statements
[1].resource
.begin()->service
, Service::wildcard
);
298 EXPECT_EQ(p
->statements
[1].resource
.begin()->region
, "*");
299 EXPECT_EQ(p
->statements
[1].resource
.begin()->account
, arbitrary_tenant
);
300 EXPECT_EQ(p
->statements
[1].resource
.begin()->resource
, "*");
301 EXPECT_TRUE(p
->statements
[1].notresource
.empty());
302 EXPECT_TRUE(p
->statements
[1].conditions
.empty());
304 EXPECT_EQ(*p
->statements
[2].sid
, "ThirdStatement");
305 EXPECT_TRUE(p
->statements
[2].princ
.empty());
306 EXPECT_TRUE(p
->statements
[2].noprinc
.empty());
307 EXPECT_EQ(p
->statements
[2].effect
, Effect::Allow
);
308 EXPECT_EQ(p
->statements
[2].action
, (s3ListMultipartUploadParts
|
309 s3ListBucket
| s3ListBucketVersions
|
311 s3ListBucketMultiPartUploads
|
312 s3GetObject
| s3GetObjectVersion
|
313 s3GetObjectAcl
| s3GetObjectVersionAcl
|
315 s3GetObjectVersionTorrent
|
316 s3GetAccelerateConfiguration
|
317 s3GetBucketAcl
| s3GetBucketCORS
|
318 s3GetBucketVersioning
|
319 s3GetBucketRequestPayment
|
320 s3GetBucketLocation
|
322 s3GetBucketNotification
|
326 s3GetLifecycleConfiguration
|
327 s3GetReplicationConfiguration
));
328 EXPECT_EQ(p
->statements
[2].notaction
, s3None
);
329 ASSERT_FALSE(p
->statements
[2].resource
.empty());
330 ASSERT_EQ(p
->statements
[2].resource
.size(), 2U);
331 EXPECT_EQ(p
->statements
[2].resource
.begin()->partition
, Partition::aws
);
332 EXPECT_EQ(p
->statements
[2].resource
.begin()->service
, Service::s3
);
333 EXPECT_TRUE(p
->statements
[2].resource
.begin()->region
.empty());
334 EXPECT_EQ(p
->statements
[2].resource
.begin()->account
, arbitrary_tenant
);
335 EXPECT_EQ(p
->statements
[2].resource
.begin()->resource
, "confidential-data");
336 EXPECT_EQ((p
->statements
[2].resource
.begin() + 1)->partition
,
338 EXPECT_EQ((p
->statements
[2].resource
.begin() + 1)->service
, Service::s3
);
339 EXPECT_TRUE((p
->statements
[2].resource
.begin() + 1)->region
.empty());
340 EXPECT_EQ((p
->statements
[2].resource
.begin() + 1)->account
,
342 EXPECT_EQ((p
->statements
[2].resource
.begin() + 1)->resource
,
343 "confidential-data/*");
344 EXPECT_TRUE(p
->statements
[2].notresource
.empty());
345 ASSERT_FALSE(p
->statements
[2].conditions
.empty());
346 ASSERT_EQ(p
->statements
[2].conditions
.size(), 1U);
347 EXPECT_EQ(p
->statements
[2].conditions
[0].op
, TokenID::Bool
);
348 EXPECT_EQ(p
->statements
[2].conditions
[0].key
, "aws:MultiFactorAuthPresent");
349 EXPECT_FALSE(p
->statements
[2].conditions
[0].ifexists
);
350 ASSERT_FALSE(p
->statements
[2].conditions
[0].vals
.empty());
351 EXPECT_EQ(p
->statements
[2].conditions
[0].vals
.size(), 1U);
352 EXPECT_EQ(p
->statements
[2].conditions
[0].vals
[0], "true");
355 TEST_F(PolicyTest
, Eval3
) {
356 auto p
= Policy(cct
.get(), arbitrary_tenant
,
357 bufferlist::static_from_string(example3
));
359 Environment tr
= { { "aws:MultiFactorAuthPresent", "true" } };
360 Environment fa
= { { "aws:MultiFactorAuthPresent", "false" } };
362 auto s3allow
= (s3ListMultipartUploadParts
| s3ListBucket
|
363 s3ListBucketVersions
| s3ListAllMyBuckets
|
364 s3ListBucketMultiPartUploads
| s3GetObject
|
365 s3GetObjectVersion
| s3GetObjectAcl
| s3GetObjectVersionAcl
|
366 s3GetObjectTorrent
| s3GetObjectVersionTorrent
|
367 s3GetAccelerateConfiguration
| s3GetBucketAcl
|
368 s3GetBucketCORS
| s3GetBucketVersioning
|
369 s3GetBucketRequestPayment
| s3GetBucketLocation
|
370 s3GetBucketPolicy
| s3GetBucketNotification
|
371 s3GetBucketLogging
| s3GetBucketTagging
|
372 s3GetBucketWebsite
| s3GetLifecycleConfiguration
|
373 s3GetReplicationConfiguration
);
375 EXPECT_EQ(p
.eval(em
, none
, s3PutBucketPolicy
,
376 ARN(Partition::aws
, Service::s3
,
377 "", arbitrary_tenant
, "mybucket")),
380 EXPECT_EQ(p
.eval(em
, none
, s3PutBucketPolicy
,
381 ARN(Partition::aws
, Service::s3
,
382 "", arbitrary_tenant
, "mybucket")),
386 for (auto i
= 0ULL; i
< s3Count
; ++i
) {
388 if ((op
== s3ListAllMyBuckets
) || (op
== s3PutBucketPolicy
)) {
392 EXPECT_EQ(p
.eval(em
, none
, op
,
393 ARN(Partition::aws
, Service::s3
,
394 "", arbitrary_tenant
, "confidential-data")),
396 EXPECT_EQ(p
.eval(tr
, none
, op
,
397 ARN(Partition::aws
, Service::s3
,
398 "", arbitrary_tenant
, "confidential-data")),
399 op
& s3allow
? Effect::Allow
: Effect::Pass
);
400 EXPECT_EQ(p
.eval(fa
, none
, op
,
401 ARN(Partition::aws
, Service::s3
,
402 "", arbitrary_tenant
, "confidential-data")),
405 EXPECT_EQ(p
.eval(em
, none
, op
,
406 ARN(Partition::aws
, Service::s3
,
407 "", arbitrary_tenant
, "confidential-data/moo")),
409 EXPECT_EQ(p
.eval(tr
, none
, op
,
410 ARN(Partition::aws
, Service::s3
,
411 "", arbitrary_tenant
, "confidential-data/moo")),
412 op
& s3allow
? Effect::Allow
: Effect::Pass
);
413 EXPECT_EQ(p
.eval(fa
, none
, op
,
414 ARN(Partition::aws
, Service::s3
,
415 "", arbitrary_tenant
, "confidential-data/moo")),
418 EXPECT_EQ(p
.eval(em
, none
, op
,
419 ARN(Partition::aws
, Service::s3
,
420 "", arbitrary_tenant
, "really-confidential-data")),
422 EXPECT_EQ(p
.eval(tr
, none
, op
,
423 ARN(Partition::aws
, Service::s3
,
424 "", arbitrary_tenant
, "really-confidential-data")),
426 EXPECT_EQ(p
.eval(fa
, none
, op
,
427 ARN(Partition::aws
, Service::s3
,
428 "", arbitrary_tenant
, "really-confidential-data")),
431 EXPECT_EQ(p
.eval(em
, none
, op
,
432 ARN(Partition::aws
, Service::s3
,
433 "", arbitrary_tenant
,
434 "really-confidential-data/moo")), Effect::Pass
);
435 EXPECT_EQ(p
.eval(tr
, none
, op
,
436 ARN(Partition::aws
, Service::s3
,
437 "", arbitrary_tenant
,
438 "really-confidential-data/moo")), Effect::Pass
);
439 EXPECT_EQ(p
.eval(fa
, none
, op
,
440 ARN(Partition::aws
, Service::s3
,
441 "", arbitrary_tenant
,
442 "really-confidential-data/moo")), Effect::Pass
);
447 const string
PolicyTest::arbitrary_tenant
= "arbitrary_tenant";
448 string
PolicyTest::example1
= R
"(
450 "Version
": "2012-10-17",
453 "Action
": "s3
:ListBucket
",
454 "Resource
": "arn
:aws
:s3:::example_bucket
"
459 string
PolicyTest::example2
= R
"(
461 "Version
": "2012-10-17",
462 "Id
": "S3
-Account
-Permissions
",
466 "Principal
": {"AWS
": ["arn
:aws
:iam::ACCOUNT
-ID
-WITHOUT
-HYPHENS
:root
"]},
469 "arn
:aws
:s3:::mybucket
",
470 "arn
:aws
:s3:::mybucket
/*"
476 string PolicyTest::example3 = R"(
478 "Version": "2012-10-17",
481 "Sid": "FirstStatement",
483 "Action": ["s3:PutBucketPolicy"],
487 "Sid": "SecondStatement",
489 "Action": "s3:ListAllMyBuckets",
493 "Sid": "ThirdStatement",
500 "arn:aws:s3:::confidential-data",
501 "arn:aws:s3:::confidential-data/*"
503 "Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}