Add Permission tests for policies that only allow specific Buckets (#1538)
This commit is contained in:
266
portal-ui/tests/permissions/bucketSpecific.ts
Normal file
266
portal-ui/tests/permissions/bucketSpecific.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as roles from "../utils/roles";
|
||||
import * as elements from "../utils/elements";
|
||||
import * as functions from "../utils/functions";
|
||||
import { bucketsElement, logoutItem } from "../utils/elements-menu";
|
||||
import { namedTestBucketBrowseButtonFor, namedManageButtonFor} from "../utils/functions";
|
||||
import { Selector } from "testcafe";
|
||||
import * as constants from "../utils/constants";
|
||||
|
||||
const TEST_BUCKET_NAME_SPECIFIC = "specific-bucket";
|
||||
|
||||
fixture("For user with permissions that only allow specific Buckets").page("http://localhost:9090");
|
||||
|
||||
test("Buckets sidebar item exists", async (t) => {
|
||||
const bucketsExist = bucketsElement.with({ boundTestRun: t }).exists;
|
||||
await t.useRole(roles.bucketSpecific).expect(bucketsExist).ok();
|
||||
});
|
||||
|
||||
// Bucket assign policy tests
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-1`);
|
||||
})("A readonly policy can be assigned to a bucket", async (t) => {
|
||||
await t
|
||||
// We need to log back in after we use the admin account to create bucket,
|
||||
// using the specific role we use in this module
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-1`))
|
||||
.click(elements.bucketAccessRulesTab)
|
||||
.click(elements.addAccessRuleButton)
|
||||
.typeText(elements.bucketsPrefixInput, "readonlytest")
|
||||
.click(elements.bucketsAccessInput)
|
||||
.click(elements.bucketsAccessReadOnlyInput)
|
||||
.click(elements.saveButton);
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket
|
||||
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-1`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-2`);
|
||||
})("A writeonly policy can be assigned to a bucket", async (t) => {
|
||||
await t
|
||||
// We need to log back in after we use the admin account to create bucket,
|
||||
// using the specific role we use in this module
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-2`))
|
||||
.click(elements.bucketAccessRulesTab)
|
||||
.click(elements.addAccessRuleButton)
|
||||
.typeText(elements.bucketsPrefixInput, "writeonlytest")
|
||||
.click(elements.bucketsAccessInput)
|
||||
.click(elements.bucketsAccessWriteOnlyInput)
|
||||
.click(elements.saveButton);
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket
|
||||
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-2`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-3`);
|
||||
})("A readwrite policy can be assigned to a bucket", async (t) => {
|
||||
await t
|
||||
// We need to log back in after we use the admin account to create bucket,
|
||||
// using the specific role we use in this module
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedManageButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-3`))
|
||||
.click(elements.bucketAccessRulesTab)
|
||||
.click(elements.addAccessRuleButton)
|
||||
.typeText(elements.bucketsPrefixInput, "readwritetest")
|
||||
.click(elements.bucketsAccessInput)
|
||||
.click(elements.bucketsAccessReadWriteInput)
|
||||
.click(elements.saveButton);
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket
|
||||
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-3`);
|
||||
});
|
||||
|
||||
// Bucket read tests
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-4`);
|
||||
})("Browse button exists", async (t) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.expect(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-4`).exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-4`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-5`);
|
||||
})("Bucket access is set to R", async (t) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.expect(
|
||||
Selector("h1")
|
||||
.withText(`${TEST_BUCKET_NAME_SPECIFIC}-5`)
|
||||
.parent(1)
|
||||
.find("p")
|
||||
.nth(-1).innerText
|
||||
)
|
||||
.eql("Access: R");
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-5`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-6`);
|
||||
await t
|
||||
.useRole(roles.admin)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt")
|
||||
.click(logoutItem);
|
||||
})("Object list table is enabled", async (t) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketRead)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-6`))
|
||||
.expect(elements.table.exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-6`);
|
||||
});
|
||||
|
||||
// Bucket write tests
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-7`);
|
||||
})("Browse button exists", async (t) => {
|
||||
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-7`);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.expect(testBucketBrowseButton.exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-7`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-8`);
|
||||
})("Bucket access is set to R/W", async (t) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.expect(
|
||||
Selector("h1")
|
||||
.withText(`${TEST_BUCKET_NAME_SPECIFIC}-8`)
|
||||
.parent(1)
|
||||
.find("p")
|
||||
.nth(-1).innerText
|
||||
)
|
||||
.eql("Access: R/W");
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-8`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-9`);
|
||||
})("Upload button exists", async (t) => {
|
||||
const uploadExists = elements.uploadButton.exists;
|
||||
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-9`);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(testBucketBrowseButton)
|
||||
.expect(uploadExists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-9`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-10`);
|
||||
})("Object can be uploaded to a bucket", async (t) => {
|
||||
const testBucketBrowseButton = namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-10`);
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(testBucketBrowseButton)
|
||||
// Upload object to bucket
|
||||
.setFilesToUpload(elements.uploadInput, "../uploads/test.txt");
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-10`);
|
||||
});
|
||||
|
||||
test
|
||||
.before(async (t) => {
|
||||
// Create a bucket
|
||||
await functions.setUpNamedBucket(t, `${TEST_BUCKET_NAME_SPECIFIC}-11`);
|
||||
})("Object list table is disabled", async (t) => {
|
||||
await t
|
||||
.useRole(roles.bucketSpecific)
|
||||
.navigateTo("http://localhost:9090/buckets")
|
||||
.click(namedTestBucketBrowseButtonFor(`${TEST_BUCKET_NAME_SPECIFIC}-11`))
|
||||
.expect(elements.bucketsTableDisabled.exists)
|
||||
.ok();
|
||||
})
|
||||
.after(async (t) => {
|
||||
// Cleanup created bucket and corresponding uploads
|
||||
await functions.cleanUpNamedBucketAndUploads(t, `${TEST_BUCKET_NAME_SPECIFIC}-11`);
|
||||
});
|
||||
43
portal-ui/tests/policies/bucketSpecific.json
Normal file
43
portal-ui/tests/policies/bucketSpecific.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::specific-bucket-1/*",
|
||||
"arn:aws:s3:::specific-bucket-2/*",
|
||||
"arn:aws:s3:::specific-bucket-3/*",
|
||||
"arn:aws:s3:::specific-bucket-4/*",
|
||||
"arn:aws:s3:::specific-bucket-5/*",
|
||||
"arn:aws:s3:::specific-bucket-6/*",
|
||||
"arn:aws:s3:::specific-bucket-7/*",
|
||||
"arn:aws:s3:::specific-bucket-8/*",
|
||||
"arn:aws:s3:::specific-bucket-9/*",
|
||||
"arn:aws:s3:::specific-bucket-10/*",
|
||||
"arn:aws:s3:::specific-bucket-11/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:CreateBucket"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::specific-bucket-1",
|
||||
"arn:aws:s3:::specific-bucket-2",
|
||||
"arn:aws:s3:::specific-bucket-3",
|
||||
"arn:aws:s3:::specific-bucket-4",
|
||||
"arn:aws:s3:::specific-bucket-5",
|
||||
"arn:aws:s3:::specific-bucket-6",
|
||||
"arn:aws:s3:::specific-bucket-7",
|
||||
"arn:aws:s3:::specific-bucket-8",
|
||||
"arn:aws:s3:::specific-bucket-9",
|
||||
"arn:aws:s3:::specific-bucket-10",
|
||||
"arn:aws:s3:::specific-bucket-11"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -26,6 +26,7 @@ create_policies() {
|
||||
mc admin policy add minio bucketassignpolicy-$TIMESTAMP portal-ui/tests/policies/bucketAssignPolicy.json
|
||||
mc admin policy add minio bucketread-$TIMESTAMP portal-ui/tests/policies/bucketRead.json
|
||||
mc admin policy add minio bucketwrite-$TIMESTAMP portal-ui/tests/policies/bucketWrite.json
|
||||
mc admin policy add minio bucketspecific-$TIMESTAMP portal-ui/tests/policies/bucketSpecific.json
|
||||
mc admin policy add minio dashboard-$TIMESTAMP portal-ui/tests/policies/dashboard.json
|
||||
mc admin policy add minio diagnostics-$TIMESTAMP portal-ui/tests/policies/diagnostics.json
|
||||
mc admin policy add minio groups-$TIMESTAMP portal-ui/tests/policies/groups.json
|
||||
@@ -45,6 +46,7 @@ create_users() {
|
||||
mc admin user add minio bucketassignpolicy-$TIMESTAMP bucketassignpolicy
|
||||
mc admin user add minio bucketread-$TIMESTAMP bucketread
|
||||
mc admin user add minio bucketwrite-$TIMESTAMP bucketwrite
|
||||
mc admin user add minio bucketspecific-$TIMESTAMP bucketspecific
|
||||
mc admin user add minio dashboard-$TIMESTAMP dashboard
|
||||
mc admin user add minio diagnostics-$TIMESTAMP diagnostics
|
||||
mc admin user add minio groups-$TIMESTAMP groups1234
|
||||
@@ -68,6 +70,7 @@ assign_policies() {
|
||||
mc admin policy set minio bucketassignpolicy-$TIMESTAMP user=bucketassignpolicy-$TIMESTAMP
|
||||
mc admin policy set minio bucketread-$TIMESTAMP user=bucketread-$TIMESTAMP
|
||||
mc admin policy set minio bucketwrite-$TIMESTAMP user=bucketwrite-$TIMESTAMP
|
||||
mc admin policy set minio bucketspecific-$TIMESTAMP user=bucketspecific-$TIMESTAMP
|
||||
mc admin policy set minio dashboard-$TIMESTAMP user=dashboard-$TIMESTAMP
|
||||
mc admin policy set minio diagnostics-$TIMESTAMP user=diagnostics-$TIMESTAMP
|
||||
mc admin policy set minio groups-$TIMESTAMP user=groups-$TIMESTAMP
|
||||
|
||||
@@ -23,6 +23,10 @@ import { logoutItem } from "./elements-menu";
|
||||
import * as Minio from "minio";
|
||||
|
||||
export const setUpBucket = (t, modifier) => {
|
||||
return setUpNamedBucket(t, `${constants.TEST_BUCKET_NAME}-${modifier}`);
|
||||
};
|
||||
|
||||
export const setUpNamedBucket = (t, name) => {
|
||||
const minioClient = new Minio.Client({
|
||||
endPoint: "localhost",
|
||||
port: 9000,
|
||||
@@ -33,21 +37,25 @@ export const setUpBucket = (t, modifier) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
minioClient
|
||||
.makeBucket(`${constants.TEST_BUCKET_NAME}-${modifier}`, "us-east-1")
|
||||
.makeBucket(name, "us-east-1")
|
||||
.then(resolve)
|
||||
.catch(resolve);
|
||||
});
|
||||
};
|
||||
|
||||
export const manageButtonFor = (modifier) => {
|
||||
export const namedManageButtonFor = (name) => {
|
||||
return Selector("h1")
|
||||
.withText(`${constants.TEST_BUCKET_NAME}-${modifier}`)
|
||||
.withText(name)
|
||||
.parent(4)
|
||||
.find("button:enabled")
|
||||
.withText("Manage");
|
||||
}
|
||||
|
||||
export const manageButtonFor = (modifier) => {
|
||||
return namedManageButtonFor(`${constants.TEST_BUCKET_NAME}-${modifier}`);
|
||||
};
|
||||
|
||||
export const cleanUpBucket = (t, modifier) => {
|
||||
export const cleanUpNamedBucket = (t, name) => {
|
||||
const minioClient = new Minio.Client({
|
||||
endPoint: "localhost",
|
||||
port: 9000,
|
||||
@@ -56,24 +64,30 @@ export const cleanUpBucket = (t, modifier) => {
|
||||
secretKey: "minioadmin",
|
||||
});
|
||||
|
||||
return minioClient.removeBucket(`${constants.TEST_BUCKET_NAME}-${modifier}`);
|
||||
return minioClient.removeBucket(name);
|
||||
};
|
||||
|
||||
export const testBucketBrowseButtonFor = (modifier) => {
|
||||
export const cleanUpBucket = (t, modifier) => {
|
||||
return cleanUpNamedBucket(t, `${constants.TEST_BUCKET_NAME}-${modifier}`);
|
||||
};
|
||||
|
||||
export const namedTestBucketBrowseButtonFor = (name) => {
|
||||
return Selector("h1")
|
||||
.withText(`${constants.TEST_BUCKET_NAME}-${modifier}`)
|
||||
.withText(name)
|
||||
.parent(4)
|
||||
.find("button:enabled")
|
||||
.withText("Browse");
|
||||
};
|
||||
|
||||
export const testBucketBrowseButtonFor = (modifier) => {
|
||||
return namedTestBucketBrowseButtonFor(`${constants.TEST_BUCKET_NAME}-${modifier}`);
|
||||
};
|
||||
|
||||
export const uploadFilesButton = () => {
|
||||
return Selector("button").withText("Upload Files");
|
||||
};
|
||||
|
||||
export const cleanUpBucketAndUploads = (t, modifier) => {
|
||||
const bucket = `${constants.TEST_BUCKET_NAME}-${modifier}`;
|
||||
|
||||
export const cleanUpNamedBucketAndUploads = (t, bucket) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const minioClient = new Minio.Client({
|
||||
endPoint: "localhost",
|
||||
@@ -98,6 +112,11 @@ export const cleanUpBucketAndUploads = (t, modifier) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const cleanUpBucketAndUploads = (t, modifier) => {
|
||||
const bucket = `${constants.TEST_BUCKET_NAME}-${modifier}`;
|
||||
return cleanUpNamedBucketAndUploads(t, bucket)
|
||||
};
|
||||
|
||||
export const createUser = (t) => {
|
||||
return t
|
||||
.useRole(roles.admin)
|
||||
|
||||
@@ -53,6 +53,17 @@ export const bucketWrite = Role(
|
||||
{ preserveUrl: true }
|
||||
);
|
||||
|
||||
export const bucketSpecific = Role(
|
||||
loginUrl,
|
||||
async (t) => {
|
||||
await t
|
||||
.typeText("#accessKey", "bucketspecific-" + unixTimestamp)
|
||||
.typeText("#secretKey", "bucketspecific")
|
||||
.click(submitButton);
|
||||
},
|
||||
{ preserveUrl: true }
|
||||
);
|
||||
|
||||
export const bucketWritePrefixOnly = Role(
|
||||
loginUrl,
|
||||
async (t) => {
|
||||
|
||||
Reference in New Issue
Block a user