]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/rgw/test_rgw_iam_policy.cc
cc512e0b0c3ff00a5d086db7768124336472165a
[ceph.git] / ceph / src / test / rgw / test_rgw_iam_policy.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2015 Red Hat
7 *
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.
12 *
13 */
14
15 #include <string>
16
17 #include <boost/intrusive_ptr.hpp>
18 #include <boost/optional.hpp>
19
20 #include <gtest/gtest.h>
21
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"
27
28
29 using std::string;
30 using std::vector;
31
32 using boost::container::flat_set;
33 using boost::intrusive_ptr;
34 using boost::make_optional;
35 using boost::none;
36 using boost::optional;
37
38 using rgw::auth::Identity;
39 using rgw::auth::Principal;
40
41 using rgw::IAM::ARN;
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;
79
80 class FakeIdentity : public Identity {
81 const Principal id;
82 public:
83
84 FakeIdentity(Principal&& id) : id(std::move(id)) {}
85 uint32_t get_perms_from_aclspec(const aclspec_t& aclspec) const override {
86 abort();
87 return 0;
88 };
89
90 bool is_admin_of(const rgw_user& uid) const override {
91 abort();
92 return false;
93 }
94
95 bool is_owner_of(const rgw_user& uid) const override {
96 abort();
97 return false;
98 }
99
100 virtual uint32_t get_perm_mask() const override {
101 abort();
102 return 0;
103 }
104
105 void to_str(std::ostream& out) const override {
106 abort();
107 }
108
109 bool is_identity(const flat_set<Principal>& ids) const override {
110 return ids.find(id) != ids.end();
111 }
112 };
113
114 class PolicyTest : public ::testing::Test {
115 protected:
116 intrusive_ptr<CephContext> cct;
117 static const string arbitrary_tenant;
118 static string example1;
119 static string example2;
120 static string example3;
121 public:
122 PolicyTest() {
123 cct = new CephContext(CEPH_ENTITY_TYPE_CLIENT);
124 }
125 };
126
127 TEST_F(PolicyTest, Parse1) {
128 optional<Policy> p;
129
130 ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
131 bufferlist::static_from_string(example1)));
132 ASSERT_TRUE(p);
133
134 EXPECT_EQ(p->text, example1);
135 EXPECT_EQ(p->version, Version::v2012_10_17);
136 EXPECT_FALSE(p->id);
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());
154 }
155
156 TEST_F(PolicyTest, Eval1) {
157 auto p = Policy(cct.get(), arbitrary_tenant,
158 bufferlist::static_from_string(example1));
159 Environment e;
160
161 EXPECT_EQ(p.eval(e, none, s3ListBucket,
162 ARN(Partition::aws, Service::s3,
163 "", arbitrary_tenant, "example_bucket")),
164 Effect::Allow);
165
166 EXPECT_EQ(p.eval(e, none, s3PutBucketAcl,
167 ARN(Partition::aws, Service::s3,
168 "", arbitrary_tenant, "example_bucket")),
169 Effect::Pass);
170
171 EXPECT_EQ(p.eval(e, none, s3ListBucket,
172 ARN(Partition::aws, Service::s3,
173 "", arbitrary_tenant, "erroneous_bucket")),
174 Effect::Pass);
175
176 }
177
178 TEST_F(PolicyTest, Parse2) {
179 optional<Policy> p;
180
181 ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
182 bufferlist::static_from_string(example2)));
183 ASSERT_TRUE(p);
184
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,
207 Partition::aws);
208 EXPECT_EQ((p->statements[0].resource.begin() + 1)->service,
209 Service::s3);
210 EXPECT_TRUE((p->statements[0].resource.begin() + 1)->region.empty());
211 EXPECT_EQ((p->statements[0].resource.begin() + 1)->account,
212 arbitrary_tenant);
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());
216 }
217
218 TEST_F(PolicyTest, Eval2) {
219 auto p = Policy(cct.get(), arbitrary_tenant,
220 bufferlist::static_from_string(example2));
221 Environment e;
222
223 auto trueacct = FakeIdentity(
224 Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
225
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")),
232 Effect::Allow);
233 EXPECT_EQ(p.eval(e, trueacct, 1ULL << i,
234 ARN(Partition::aws, Service::s3,
235 "", arbitrary_tenant, "mybucket/myobject")),
236 Effect::Allow);
237
238 EXPECT_EQ(p.eval(e, notacct, 1ULL << i,
239 ARN(Partition::aws, Service::s3,
240 "", arbitrary_tenant, "mybucket")),
241 Effect::Pass);
242 EXPECT_EQ(p.eval(e, notacct, 1ULL << i,
243 ARN(Partition::aws, Service::s3,
244 "", arbitrary_tenant, "mybucket/myobject")),
245 Effect::Pass);
246
247 EXPECT_EQ(p.eval(e, trueacct, 1ULL << i,
248 ARN(Partition::aws, Service::s3,
249 "", arbitrary_tenant, "notyourbucket")),
250 Effect::Pass);
251 EXPECT_EQ(p.eval(e, trueacct, 1ULL << i,
252 ARN(Partition::aws, Service::s3,
253 "", arbitrary_tenant, "notyourbucket/notyourobject")),
254 Effect::Pass);
255
256 }
257 }
258
259 TEST_F(PolicyTest, Parse3) {
260 optional<Policy> p;
261
262 ASSERT_NO_THROW(p = Policy(cct.get(), arbitrary_tenant,
263 bufferlist::static_from_string(example3)));
264 ASSERT_TRUE(p);
265
266 EXPECT_EQ(p->text, example3);
267 EXPECT_EQ(p->version, Version::v2012_10_17);
268 EXPECT_FALSE(p->id);
269 ASSERT_FALSE(p->statements.empty());
270 EXPECT_EQ(p->statements.size(), 3U);
271
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());
287
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());
303
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 |
310 s3ListAllMyBuckets |
311 s3ListBucketMultiPartUploads |
312 s3GetObject | s3GetObjectVersion |
313 s3GetObjectAcl | s3GetObjectVersionAcl |
314 s3GetObjectTorrent |
315 s3GetObjectVersionTorrent |
316 s3GetAccelerateConfiguration |
317 s3GetBucketAcl | s3GetBucketCORS |
318 s3GetBucketVersioning |
319 s3GetBucketRequestPayment |
320 s3GetBucketLocation |
321 s3GetBucketPolicy |
322 s3GetBucketNotification |
323 s3GetBucketLogging |
324 s3GetBucketTagging |
325 s3GetBucketWebsite |
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,
337 Partition::aws);
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,
341 arbitrary_tenant);
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");
353 }
354
355 TEST_F(PolicyTest, Eval3) {
356 auto p = Policy(cct.get(), arbitrary_tenant,
357 bufferlist::static_from_string(example3));
358 Environment em;
359 Environment tr = { { "aws:MultiFactorAuthPresent", "true" } };
360 Environment fa = { { "aws:MultiFactorAuthPresent", "false" } };
361
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);
374
375 EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy,
376 ARN(Partition::aws, Service::s3,
377 "", arbitrary_tenant, "mybucket")),
378 Effect::Allow);
379
380 EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy,
381 ARN(Partition::aws, Service::s3,
382 "", arbitrary_tenant, "mybucket")),
383 Effect::Allow);
384
385
386 for (auto i = 0ULL; i < s3Count; ++i) {
387 auto op = 1ULL << i;
388 if ((op == s3ListAllMyBuckets) || (op == s3PutBucketPolicy)) {
389 continue;
390 }
391
392 EXPECT_EQ(p.eval(em, none, op,
393 ARN(Partition::aws, Service::s3,
394 "", arbitrary_tenant, "confidential-data")),
395 Effect::Pass);
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")),
403 Effect::Pass);
404
405 EXPECT_EQ(p.eval(em, none, op,
406 ARN(Partition::aws, Service::s3,
407 "", arbitrary_tenant, "confidential-data/moo")),
408 Effect::Pass);
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")),
416 Effect::Pass);
417
418 EXPECT_EQ(p.eval(em, none, op,
419 ARN(Partition::aws, Service::s3,
420 "", arbitrary_tenant, "really-confidential-data")),
421 Effect::Pass);
422 EXPECT_EQ(p.eval(tr, none, op,
423 ARN(Partition::aws, Service::s3,
424 "", arbitrary_tenant, "really-confidential-data")),
425 Effect::Pass);
426 EXPECT_EQ(p.eval(fa, none, op,
427 ARN(Partition::aws, Service::s3,
428 "", arbitrary_tenant, "really-confidential-data")),
429 Effect::Pass);
430
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);
443
444 }
445 }
446
447 const string PolicyTest::arbitrary_tenant = "arbitrary_tenant";
448 string PolicyTest::example1 = R"(
449 {
450 "Version": "2012-10-17",
451 "Statement": {
452 "Effect": "Allow",
453 "Action": "s3:ListBucket",
454 "Resource": "arn:aws:s3:::example_bucket"
455 }
456 }
457 )";
458
459 string PolicyTest::example2 = R"(
460 {
461 "Version": "2012-10-17",
462 "Id": "S3-Account-Permissions",
463 "Statement": [{
464 "Sid": "1",
465 "Effect": "Allow",
466 "Principal": {"AWS": ["arn:aws:iam::ACCOUNT-ID-WITHOUT-HYPHENS:root"]},
467 "Action": "s3:*",
468 "Resource": [
469 "arn:aws:s3:::mybucket",
470 "arn:aws:s3:::mybucket/*"
471 ]
472 }]
473 }
474 )";
475
476 string PolicyTest::example3 = R"(
477 {
478 "Version": "2012-10-17",
479 "Statement": [
480 {
481 "Sid": "FirstStatement",
482 "Effect": "Allow",
483 "Action": ["s3:PutBucketPolicy"],
484 "Resource": "*"
485 },
486 {
487 "Sid": "SecondStatement",
488 "Effect": "Allow",
489 "Action": "s3:ListAllMyBuckets",
490 "Resource": "*"
491 },
492 {
493 "Sid": "ThirdStatement",
494 "Effect": "Allow",
495 "Action": [
496 "s3:List*",
497 "s3:Get*"
498 ],
499 "Resource": [
500 "arn:aws:s3:::confidential-data",
501 "arn:aws:s3:::confidential-data/*"
502 ],
503 "Condition": {"Bool": {"aws:MultiFactorAuthPresent": "true"}}
504 }
505 ]
506 }
507 )";