Skip to content

Commit 9759849

Browse files
Added some fixes and updated tests
Signed-off-by: Aayush Chouhan <achouhan@redhat.com>
1 parent 35ddc87 commit 9759849

File tree

3 files changed

+58
-63
lines changed

3 files changed

+58
-63
lines changed

src/server/common_services/auth_server.js

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ function _prepare_auth_request(req) {
495495
req.has_bucket_action_permission = async function(bucket, action, bucket_path, req_query) {
496496
return has_bucket_action_permission(bucket, req.account, action, req_query, bucket_path);
497497
};
498+
499+
req.has_list_bucket_permission = function(bucket) {
500+
return has_list_bucket_permission(bucket, req.account, req.system);
501+
};
498502
}
499503

500504
function _get_auth_info(account, system, authorized_by, role, extra) {
@@ -519,6 +523,30 @@ function _get_auth_info(account, system, authorized_by, role, extra) {
519523
return response;
520524
}
521525

526+
/**
527+
* has_list_bucket_permission returns true if the account can list the bucket in ListBuckets operation
528+
* this is a synchronous function that only checks system ownership and bucket ownership
529+
*
530+
* TODO: Add IAM policy support for s3:ListAllMyBuckets action
531+
*
532+
* @param {Record<string, any>} bucket
533+
* @param {Record<string, any>} account
534+
* @param {Record<string, any>} system
535+
* @returns {boolean}
536+
*/
537+
function has_list_bucket_permission(bucket, account, system) {
538+
if (!account || !account.email) return false;
539+
540+
// system owner can list all the buckets
541+
if (system.owner.email.unwrap() === account.email.unwrap()) return true;
542+
543+
// bucket owner can list their buckets only
544+
const is_owner = (bucket.owner_account.email.unwrap() === account.email.unwrap()) ||
545+
(account.bucket_claim_owner && account.bucket_claim_owner.name.unwrap() === bucket.name.unwrap());
546+
547+
return is_owner;
548+
}
549+
522550
/**
523551
* has_bucket_action_permission returns true if the requesting account has permission to perform
524552
* the given action on the given bucket.
@@ -536,10 +564,6 @@ function _get_auth_info(account, system, authorized_by, role, extra) {
536564
* @returns {Promise<boolean>} true if the account has permission to perform the action on the bucket
537565
*/
538566
async function has_bucket_action_permission(bucket, account, action, req_query, bucket_path = "") {
539-
if (!account || !account.email) {
540-
dbg.warn('has_bucket_action_permission: account or account.email is missing');
541-
return false;
542-
}
543567
dbg.log1('has_bucket_action_permission:', bucket.name, account.email, bucket.owner_account.email);
544568

545569
// If the system owner account wants to access the bucket, allow it
@@ -563,9 +587,8 @@ async function has_bucket_action_permission(bucket, account, action, req_query,
563587
);
564588

565589
if (result === 'DENY') return false;
566-
if (result === 'ALLOW') return true;
567590

568-
return is_owner;
591+
return is_owner || result === 'ALLOW';
569592
}
570593

571594
/**

src/server/system_services/bucket_server.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,14 +1062,11 @@ async function list_buckets(req) {
10621062
let continuation_token = req.rpc_params?.continuation_token;
10631063
const max_buckets = req.rpc_params?.max_buckets;
10641064

1065-
const accessible_bucket_list = [];
1066-
await Promise.all(
1067-
system_store.data.buckets.map(async bucket => {
1068-
if (bucket.deleting) return;
1069-
const has_permission = await req.has_s3_bucket_permission(bucket, "s3:ListBucket");
1070-
if (has_permission) accessible_bucket_list.push(bucket);
1071-
})
1072-
);
1065+
// filter buckets based on ownership
1066+
const accessible_bucket_list = system_store.data.buckets.filter(bucket => {
1067+
if (bucket.deleting) return false;
1068+
return req.has_list_bucket_permission(bucket);
1069+
});
10731070

10741071
accessible_bucket_list.sort((a, b) => a.name.unwrap().localeCompare(b.name.unwrap()));
10751072

src/test/integration_tests/api/s3/test_s3_list_buckets.js

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -129,74 +129,49 @@ mocha.describe('s3_ops', function() {
129129

130130
mocha.describe('list_buckets permissions', function() {
131131
this.timeout(60000);
132-
let s3_user;
133-
const test_user = 'test-user';
134-
const user_bucket = 'user-buck';
135-
const admin_bucket = 'admin-buck';
132+
let s3_account_a;
133+
let s3_account_b;
136134

137-
mocha.before(async function() {
135+
async function create_account_and_client(name) {
138136
const account = await rpc_client.account.create_account({
139-
name: test_user,
140-
email: test_user,
141-
has_login: false,
142-
s3_access: true,
137+
name, email: name, has_login: false, s3_access: true,
143138
default_resource: coretest.POOL_LIST[0].name
144139
});
145-
146-
s3_user = new S3Client({
140+
return new S3Client({
147141
...s3_client_params,
148142
credentials: {
149143
accessKeyId: account.access_keys[0].access_key.unwrap(),
150144
secretAccessKey: account.access_keys[0].secret_key.unwrap(),
151145
}
152146
});
147+
}
153148

154-
await s3_user.send(new CreateBucketCommand({ Bucket: user_bucket }));
155-
await s3.send(new CreateBucketCommand({ Bucket: admin_bucket }));
149+
mocha.before(async function() {
150+
s3_account_a = await create_account_and_client('account-a');
151+
s3_account_b = await create_account_and_client('account-b');
152+
await s3_account_a.send(new CreateBucketCommand({ Bucket: 'bucket-a' }));
153+
await s3_account_b.send(new CreateBucketCommand({ Bucket: 'bucket-b' }));
154+
await s3.send(new CreateBucketCommand({ Bucket: 'admin-buck' }));
156155
});
157156

158157
mocha.after(async function() {
159-
await s3_user.send(new DeleteBucketCommand({ Bucket: user_bucket }));
160-
await s3.send(new DeleteBucketCommand({ Bucket: admin_bucket }));
161-
await rpc_client.account.delete_account({ email: test_user });
158+
await s3_account_a.send(new DeleteBucketCommand({ Bucket: 'bucket-a' }));
159+
await s3_account_b.send(new DeleteBucketCommand({ Bucket: 'bucket-b' }));
160+
await s3.send(new DeleteBucketCommand({ Bucket: 'admin-buck' }));
161+
await rpc_client.account.delete_account({ email: 'account-a' });
162+
await rpc_client.account.delete_account({ email: 'account-b' });
162163
});
163164

164-
mocha.it('user should list only owned buckets', async function() {
165-
const buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
166-
assert(buckets.includes(user_bucket) && !buckets.includes(admin_bucket));
165+
mocha.it('accounts should list only owned buckets', async function() {
166+
const buckets_a = (await s3_account_a.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
167+
const buckets_b = (await s3_account_b.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
168+
assert.deepStrictEqual(buckets_a, ['bucket-a']);
169+
assert.deepStrictEqual(buckets_b, ['bucket-b']);
167170
});
168171

169-
mocha.it('admin should list all buckets', async function() {
172+
mocha.it('admin should lists all the buckets', async function() {
170173
const buckets = (await s3.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
171-
assert(buckets.length >= 2);
172-
assert(buckets.includes(user_bucket));
173-
assert(buckets.includes(admin_bucket));
174-
});
175-
176-
mocha.it('bucket policy grants list access', async function() {
177-
let buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
178-
assert(!buckets.includes(admin_bucket));
179-
180-
await rpc_client.bucket.put_bucket_policy({
181-
name: admin_bucket,
182-
policy: {
183-
Version: '2012-10-17',
184-
Statement: [{
185-
Effect: 'Allow',
186-
Principal: { AWS: test_user },
187-
Action: ['s3:ListBucket'],
188-
Resource: [`arn:aws:s3:::${admin_bucket}`]
189-
}]
190-
}
191-
});
192-
193-
buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
194-
assert(buckets.includes(admin_bucket));
195-
196-
await rpc_client.bucket.delete_bucket_policy({ name: admin_bucket });
197-
198-
buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
199-
assert(!buckets.includes(admin_bucket));
174+
assert(buckets.includes('bucket-a') && buckets.includes('bucket-b') && buckets.includes('admin-buck'));
200175
});
201176
});
202177

0 commit comments

Comments
 (0)