Skip to content

Commit df17d31

Browse files
authored
Add Permission tests for policies that only allow specific Buckets (#1538)
1 parent 302c0dd commit df17d31

File tree

5 files changed

+352
-10
lines changed

5 files changed

+352
-10
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import * as roles from "../utils/roles";
18+
import * as elements from "../utils/elements";
19+
import * as functions from "../utils/functions";
20+
import { bucketsElement, logoutItem } from "../utils/elements-menu";
21+
import { namedTestBucketBrowseButtonFor, namedManageButtonFor} from "../utils/functions";
22+
import { Selector } from "testcafe";
23+
import * as constants from "../utils/constants";
24+
25+
const TEST_BUCKET_NAME_SPECIFIC = "specific-bucket";
26+
27+
fixture("For user with permissions that only allow specific Buckets").page("http://localhost:9090");
28+
29+
test("Buckets sidebar item exists", async (t) => {
30+
const bucketsExist = bucketsElement.with({ boundTestRun: t }).exists;
31+
await t.useRole(roles.bucketSpecific).expect(bucketsExist).ok();
32+
});
33+
34+
// Bucket assign policy tests
35+
36+
test
37+
.before(async (t) => {
38+
// Create a bucket
39+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-1`);
40+
})("A readonly policy can be assigned to a bucket", async (t) => {
41+
await t
42+
// We need to log back in after we use the admin account to create bucket,
43+
// using the specific role we use in this module
44+
.useRole(roles.bucketSpecific)
45+
.navigateTo("http://localhost:9090/buckets")
46+
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-1`))
47+
.click(elements.bucketAccessRulesTab)
48+
.click(elements.addAccessRuleButton)
49+
.typeText(elements.bucketsPrefixInput, "readonlytest")
50+
.click(elements.bucketsAccessInput)
51+
.click(elements.bucketsAccessReadOnlyInput)
52+
.click(elements.saveButton);
53+
})
54+
.after(async (t) => {
55+
// Cleanup created bucket
56+
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-1`);
57+
});
58+
59+
test
60+
.before(async (t) => {
61+
// Create a bucket
62+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-2`);
63+
})("A writeonly policy can be assigned to a bucket", async (t) => {
64+
await t
65+
// We need to log back in after we use the admin account to create bucket,
66+
// using the specific role we use in this module
67+
.useRole(roles.bucketSpecific)
68+
.navigateTo("http://localhost:9090/buckets")
69+
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-2`))
70+
.click(elements.bucketAccessRulesTab)
71+
.click(elements.addAccessRuleButton)
72+
.typeText(elements.bucketsPrefixInput, "writeonlytest")
73+
.click(elements.bucketsAccessInput)
74+
.click(elements.bucketsAccessWriteOnlyInput)
75+
.click(elements.saveButton);
76+
})
77+
.after(async (t) => {
78+
// Cleanup created bucket
79+
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-2`);
80+
});
81+
82+
test
83+
.before(async (t) => {
84+
// Create a bucket
85+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-3`);
86+
})("A readwrite policy can be assigned to a bucket", async (t) => {
87+
await t
88+
// We need to log back in after we use the admin account to create bucket,
89+
// using the specific role we use in this module
90+
.useRole(roles.bucketSpecific)
91+
.navigateTo("http://localhost:9090/buckets")
92+
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-3`))
93+
.click(elements.bucketAccessRulesTab)
94+
.click(elements.addAccessRuleButton)
95+
.typeText(elements.bucketsPrefixInput, "readwritetest")
96+
.click(elements.bucketsAccessInput)
97+
.click(elements.bucketsAccessReadWriteInput)
98+
.click(elements.saveButton);
99+
})
100+
.after(async (t) => {
101+
// Cleanup created bucket
102+
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-3`);
103+
});
104+
105+
// Bucket read tests
106+
107+
test
108+
.before(async (t) => {
109+
// Create a bucket
110+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-4`);
111+
})("Browse button exists", async (t) => {
112+
await new Promise((resolve) => setTimeout(resolve, 2000));
113+
await t
114+
.useRole(roles.bucketRead)
115+
.navigateTo("http://localhost:9090/buckets")
116+
.expect(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-4`).exists)
117+
.ok();
118+
})
119+
.after(async (t) => {
120+
// Cleanup created bucket and corresponding uploads
121+
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-4`);
122+
});
123+
124+
test
125+
.before(async (t) => {
126+
// Create a bucket
127+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-5`);
128+
})("Bucket access is set to R", async (t) => {
129+
await new Promise((resolve) => setTimeout(resolve, 2000));
130+
await t
131+
.useRole(roles.bucketRead)
132+
.navigateTo("http://localhost:9090/buckets")
133+
.expect(
134+
Selector("h1")
135+
.withText(`${TEST_BUCKET_NAME_SPECIFIC}-5`)
136+
.parent(1)
137+
.find("p")
138+
.nth(-1).innerText
139+
)
140+
.eql("Access: R");
141+
})
142+
.after(async (t) => {
143+
// Cleanup created bucket and corresponding uploads
144+
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-5`);
145+
});
146+
147+
test
148+
.before(async (t) => {
149+
// Create a bucket
150+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-6`);
151+
await t
152+
.useRole(roles.admin)
153+
.navigateTo("http://localhost:9090/buckets")
154+
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
155+
// Upload object to bucket
156+
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
157+
.click(logoutItem);
158+
})("Object list table is enabled", async (t) => {
159+
await new Promise((resolve) => setTimeout(resolve, 2000));
160+
await t
161+
.useRole(roles.bucketRead)
162+
.navigateTo("http://localhost:9090/buckets")
163+
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
164+
.expect(elements.table.exists)
165+
.ok();
166+
})
167+
.after(async (t) => {
168+
// Cleanup created bucket and corresponding uploads
169+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-6`);
170+
});
171+
172+
// Bucket write tests
173+
174+
test
175+
.before(async (t) => {
176+
// Create a bucket
177+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-7`);
178+
})("Browse button exists", async (t) => {
179+
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-7`);
180+
await t
181+
.useRole(roles.bucketSpecific)
182+
.navigateTo("http://localhost:9090/buckets")
183+
.expect(testBucketBrowseButton.exists)
184+
.ok();
185+
})
186+
.after(async (t) => {
187+
// Cleanup created bucket and corresponding uploads
188+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-7`);
189+
});
190+
191+
test
192+
.before(async (t) => {
193+
// Create a bucket
194+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-8`);
195+
})("Bucket access is set to R/W", async (t) => {
196+
await new Promise((resolve) => setTimeout(resolve, 2000));
197+
await t
198+
.useRole(roles.bucketSpecific)
199+
.navigateTo("http://localhost:9090/buckets")
200+
.expect(
201+
Selector("h1")
202+
.withText(`${TEST_BUCKET_NAME_SPECIFIC}-8`)
203+
.parent(1)
204+
.find("p")
205+
.nth(-1).innerText
206+
)
207+
.eql("Access: R/W");
208+
})
209+
.after(async (t) => {
210+
// Cleanup created bucket and corresponding uploads
211+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-8`);
212+
});
213+
214+
test
215+
.before(async (t) => {
216+
// Create a bucket
217+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-9`);
218+
})("Upload button exists", async (t) => {
219+
const uploadExists = elements.uploadButton.exists;
220+
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-9`);
221+
await t
222+
.useRole(roles.bucketSpecific)
223+
.navigateTo("http://localhost:9090/buckets")
224+
.click(testBucketBrowseButton)
225+
.expect(uploadExists)
226+
.ok();
227+
})
228+
.after(async (t) => {
229+
// Cleanup created bucket and corresponding uploads
230+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-9`);
231+
});
232+
233+
test
234+
.before(async (t) => {
235+
// Create a bucket
236+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-10`);
237+
})("Object can be uploaded to a bucket", async (t) => {
238+
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-10`);
239+
await t
240+
.useRole(roles.bucketSpecific)
241+
.navigateTo("http://localhost:9090/buckets")
242+
.click(testBucketBrowseButton)
243+
// Upload object to bucket
244+
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt");
245+
})
246+
.after(async (t) => {
247+
// Cleanup created bucket and corresponding uploads
248+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-10`);
249+
});
250+
251+
test
252+
.before(async (t) => {
253+
// Create a bucket
254+
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-11`);
255+
})("Object list table is disabled", async (t) => {
256+
await t
257+
.useRole(roles.bucketSpecific)
258+
.navigateTo("http://localhost:9090/buckets")
259+
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-11`))
260+
.expect(elements.bucketsTableDisabled.exists)
261+
.ok();
262+
})
263+
.after(async (t) => {
264+
// Cleanup created bucket and corresponding uploads
265+
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-11`);
266+
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [
4+
{
5+
"Effect": "Allow",
6+
"Action": [
7+
"s3:*"
8+
],
9+
"Resource": [
10+
"arn:aws:s3:::specific-bucket-1/*",
11+
"arn:aws:s3:::specific-bucket-2/*",
12+
"arn:aws:s3:::specific-bucket-3/*",
13+
"arn:aws:s3:::specific-bucket-4/*",
14+
"arn:aws:s3:::specific-bucket-5/*",
15+
"arn:aws:s3:::specific-bucket-6/*",
16+
"arn:aws:s3:::specific-bucket-7/*",
17+
"arn:aws:s3:::specific-bucket-8/*",
18+
"arn:aws:s3:::specific-bucket-9/*",
19+
"arn:aws:s3:::specific-bucket-10/*",
20+
"arn:aws:s3:::specific-bucket-11/*"
21+
]
22+
},
23+
{
24+
"Effect": "Allow",
25+
"Action": [
26+
"s3:CreateBucket"
27+
],
28+
"Resource": [
29+
"arn:aws:s3:::specific-bucket-1",
30+
"arn:aws:s3:::specific-bucket-2",
31+
"arn:aws:s3:::specific-bucket-3",
32+
"arn:aws:s3:::specific-bucket-4",
33+
"arn:aws:s3:::specific-bucket-5",
34+
"arn:aws:s3:::specific-bucket-6",
35+
"arn:aws:s3:::specific-bucket-7",
36+
"arn:aws:s3:::specific-bucket-8",
37+
"arn:aws:s3:::specific-bucket-9",
38+
"arn:aws:s3:::specific-bucket-10",
39+
"arn:aws:s3:::specific-bucket-11"
40+
]
41+
}
42+
]
43+
}

portal-ui/tests/scripts/common.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ create_policies() {
2626
mc admin policy add minio bucketassignpolicy-$TIMESTAMP portal-ui/tests/policies/bucketAssignPolicy.json
2727
mc admin policy add minio bucketread-$TIMESTAMP portal-ui/tests/policies/bucketRead.json
2828
mc admin policy add minio bucketwrite-$TIMESTAMP portal-ui/tests/policies/bucketWrite.json
29+
mc admin policy add minio bucketspecific-$TIMESTAMP portal-ui/tests/policies/bucketSpecific.json
2930
mc admin policy add minio dashboard-$TIMESTAMP portal-ui/tests/policies/dashboard.json
3031
mc admin policy add minio diagnostics-$TIMESTAMP portal-ui/tests/policies/diagnostics.json
3132
mc admin policy add minio groups-$TIMESTAMP portal-ui/tests/policies/groups.json
@@ -45,6 +46,7 @@ create_users() {
4546
mc admin user add minio bucketassignpolicy-$TIMESTAMP bucketassignpolicy
4647
mc admin user add minio bucketread-$TIMESTAMP bucketread
4748
mc admin user add minio bucketwrite-$TIMESTAMP bucketwrite
49+
mc admin user add minio bucketspecific-$TIMESTAMP bucketspecific
4850
mc admin user add minio dashboard-$TIMESTAMP dashboard
4951
mc admin user add minio diagnostics-$TIMESTAMP diagnostics
5052
mc admin user add minio groups-$TIMESTAMP groups1234
@@ -68,6 +70,7 @@ assign_policies() {
6870
mc admin policy set minio bucketassignpolicy-$TIMESTAMP user=bucketassignpolicy-$TIMESTAMP
6971
mc admin policy set minio bucketread-$TIMESTAMP user=bucketread-$TIMESTAMP
7072
mc admin policy set minio bucketwrite-$TIMESTAMP user=bucketwrite-$TIMESTAMP
73+
mc admin policy set minio bucketspecific-$TIMESTAMP user=bucketspecific-$TIMESTAMP
7174
mc admin policy set minio dashboard-$TIMESTAMP user=dashboard-$TIMESTAMP
7275
mc admin policy set minio diagnostics-$TIMESTAMP user=diagnostics-$TIMESTAMP
7376
mc admin policy set minio groups-$TIMESTAMP user=groups-$TIMESTAMP

0 commit comments

Comments
 (0)