Skip to content

Commit 6263b2d

Browse files
committed
✅ add new functional tests to cover more use case
Issue: CLDSRV-813
1 parent 4920538 commit 6263b2d

1 file changed

Lines changed: 177 additions & 60 deletions

File tree

  • tests/functional/aws-node-sdk/test/bucket

tests/functional/aws-node-sdk/test/bucket/get.js

Lines changed: 177 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ const withV4 = require('../support/withV4');
66
const BucketUtility = require('../../lib/utility/bucket-util');
77
const bucketSchema = require('../../schema/bucket');
88
const bucketSchemaV2 = require('../../schema/bucketV2');
9-
const { generateToken, decryptToken } =
10-
require('../../../../../lib/api/apiUtils/object/continueToken');
9+
const { generateToken, decryptToken } = require('../../../../../lib/api/apiUtils/object/continueToken');
10+
const AWS = require('aws-sdk');
11+
const { IAM } = AWS;
12+
const getConfig = require('../support/config');
13+
const { config } = require('../../../../../lib/Config');
14+
15+
const vaultHost = config.vaultd?.host || 'localhost';
1116

1217
const tests = [
1318
{
@@ -374,64 +379,6 @@ describe('GET Bucket - AWS.S3.listObjects', () => {
374379
});
375380
});
376381

377-
it('should manage the x-amz-optional-attributes header', async () => {
378-
const s3 = bucketUtil.s3;
379-
const Bucket = bucketName;
380-
381-
await s3.putObject({
382-
Bucket,
383-
Key: 'super-power-object',
384-
Metadata: {
385-
Department: 'sales',
386-
HR: 'true',
387-
},
388-
}).promise();
389-
390-
const result = await new Promise((resolve, reject) => {
391-
let rawXml = '';
392-
const req = s3.listObjectsV2({ Bucket });
393-
394-
req.on('build', () => {
395-
req.httpRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-*';
396-
req.httpRequest.headers['x-amz-optional-object-attributes'] += ',RestoreStatus';
397-
req.httpRequest.headers['x-amz-optional-object-attributes'] += ',x-amz-meta-department';
398-
});
399-
req.on('httpData', chunk => { rawXml += chunk; });
400-
req.on('error', err => reject(err));
401-
req.on('success', response => {
402-
parseString(rawXml, (err, parsedXml) => {
403-
if (err) {
404-
return reject(err);
405-
}
406-
407-
const contents = response.data.Contents;
408-
const parsedContents = parsedXml.ListBucketResult.Contents;
409-
410-
if (!contents || !parsedContents) {
411-
return resolve(response.data);
412-
}
413-
414-
if (parsedContents[0]?.['x-amz-meta-department']) {
415-
contents[0]['x-amz-meta-department'] = parsedContents[0]['x-amz-meta-department'][0];
416-
}
417-
418-
if (parsedContents[0]?.['x-amz-meta-hr']) {
419-
contents[0]['x-amz-meta-hr'] = parsedContents[0]['x-amz-meta-hr'][0];
420-
}
421-
422-
return resolve(response.data);
423-
});
424-
});
425-
426-
req.send();
427-
});
428-
429-
assert.strictEqual(result.Contents.length, 1);
430-
assert.strictEqual(result.Contents[0].Key, 'super-power-object');
431-
assert.strictEqual(result.Contents[0]['x-amz-meta-department'], 'sales');
432-
assert.strictEqual(result.Contents[0]['x-amz-meta-hr'], 'true');
433-
});
434-
435382
['&amp', '"quot', '\'apos', '<lt', '>gt'].forEach(k => {
436383
it(`should list objects with key ${k} as Prefix`, async () => {
437384
const s3 = bucketUtil.s3;
@@ -549,5 +496,175 @@ describe('GET Bucket - AWS.S3.listObjects', () => {
549496
decryptToken(data.NextContinuationToken), k);
550497
});
551498
});
499+
500+
describe('x-amz-optional-attributes header', () => {
501+
let policyWithoutPermission;
502+
let userWithoutPermission;
503+
let s3ClientWithoutPermission;
504+
505+
const iamConfig = getConfig('default', { region: 'us-east-1' });
506+
iamConfig.endpoint = `http://${vaultHost}:8600`;
507+
const iamClient = new IAM(iamConfig);
508+
509+
before(async () => {
510+
const policyRes = await iamClient
511+
.createPolicy({
512+
PolicyName: 'bp-bypass-policy',
513+
PolicyDocument: JSON.stringify({
514+
Version: '2012-10-17',
515+
Statement: [{
516+
Sid: 'AllowS3ListBucket',
517+
Effect: 'Allow',
518+
Action: [
519+
's3:ListBucket',
520+
],
521+
Resource: ['*'],
522+
}],
523+
}),
524+
})
525+
.promise();
526+
policyWithoutPermission = policyRes.Policy;
527+
const userRes = await iamClient.createUser({ UserName: 'user-without-permission' }).promise();
528+
userWithoutPermission = userRes.User;
529+
await iamClient
530+
.attachUserPolicy({
531+
UserName: userWithoutPermission.UserName,
532+
PolicyArn: policyWithoutPermission.Arn,
533+
})
534+
.promise();
535+
536+
const accessKeyRes = await iamClient.createAccessKey({
537+
UserName: userWithoutPermission.UserName,
538+
}).promise();
539+
const accessKey = accessKeyRes.AccessKey;
540+
const s3Config = getConfig('default', {
541+
credentials: new AWS.Credentials(accessKey.AccessKeyId, accessKey.SecretAccessKey),
542+
});
543+
s3ClientWithoutPermission = new AWS.S3(s3Config);
544+
});
545+
546+
after(async () => {
547+
await iamClient
548+
.detachUserPolicy({
549+
UserName: userWithoutPermission.UserName,
550+
PolicyArn: policyWithoutPermission.Arn,
551+
})
552+
.promise();
553+
await iamClient.deletePolicy({ PolicyArn: policyWithoutPermission.Arn }).promise();
554+
await iamClient.deleteUser({ UserName: userWithoutPermission.UserName }).promise();
555+
});
556+
557+
// eslint-disable-next-line max-len
558+
const listObjectsV2WithOptionalAttributes = async (s3, bucket, headerValue) => await new Promise((resolve, reject) => {
559+
let rawXml = '';
560+
const req = s3.listObjectsV2({ Bucket: bucket });
561+
562+
req.on('build', () => {
563+
req.httpRequest.headers['x-amz-optional-object-attributes'] = headerValue;
564+
});
565+
req.on('httpData', chunk => { rawXml += chunk; });
566+
req.on('error', err => reject(err));
567+
req.on('success', response => {
568+
parseString(rawXml, (err, parsedXml) => {
569+
if (err) {
570+
return reject(err);
571+
}
572+
573+
const contents = response.data.Contents;
574+
const parsedContents = parsedXml.ListBucketResult.Contents;
575+
576+
if (!contents || !parsedContents) {
577+
return resolve(response.data);
578+
}
579+
580+
if (parsedContents[0]?.['x-amz-meta-department']) {
581+
contents[0]['x-amz-meta-department'] = parsedContents[0]['x-amz-meta-department'][0];
582+
}
583+
584+
if (parsedContents[0]?.['x-amz-meta-hr']) {
585+
contents[0]['x-amz-meta-hr'] = parsedContents[0]['x-amz-meta-hr'][0];
586+
}
587+
588+
return resolve(response.data);
589+
});
590+
});
591+
592+
req.send();
593+
});
594+
595+
it('should return an XML if the header is set', async () => {
596+
const s3 = bucketUtil.s3;
597+
const Bucket = bucketName;
598+
599+
await s3.putObject({
600+
Bucket,
601+
Key: 'super-power-object',
602+
Metadata: {
603+
Department: 'sales',
604+
HR: 'true',
605+
},
606+
}).promise();
607+
const result = await listObjectsV2WithOptionalAttributes(
608+
s3,
609+
Bucket,
610+
'x-amz-meta-*,RestoreStatus,x-amz-meta-department',
611+
);
612+
613+
assert.strictEqual(result.Contents.length, 1);
614+
assert.strictEqual(result.Contents[0].Key, 'super-power-object');
615+
assert.strictEqual(result.Contents[0]['x-amz-meta-department'], 'sales');
616+
assert.strictEqual(result.Contents[0]['x-amz-meta-hr'], 'true');
617+
});
618+
619+
it('should reject the request if the user does not have the permission', async () => {
620+
const s3 = bucketUtil.s3;
621+
const Bucket = bucketName;
622+
623+
await s3.putObject({
624+
Bucket,
625+
Key: 'super-power-object',
626+
Metadata: {
627+
Department: 'sales',
628+
HR: 'true',
629+
},
630+
}).promise();
631+
632+
try {
633+
const result = await listObjectsV2WithOptionalAttributes(
634+
s3ClientWithoutPermission,
635+
Bucket,
636+
'x-amz-meta-*,RestoreStatus,x-amz-meta-department',
637+
);
638+
throw new Error('Request should have been rejected');
639+
} catch (err) {
640+
assert.strictEqual(err.statusCode, 403);
641+
assert.strictEqual(err.code, 'AccessDenied');
642+
}
643+
});
644+
645+
it('should return an XML if the header is only RestoreStatus even without permission', async () => {
646+
const s3 = bucketUtil.s3;
647+
const Bucket = bucketName;
648+
649+
await s3.putObject({
650+
Bucket,
651+
Key: 'super-power-object',
652+
Metadata: {
653+
Department: 'sales',
654+
HR: 'true',
655+
},
656+
}).promise();
657+
const result = await listObjectsV2WithOptionalAttributes(
658+
s3ClientWithoutPermission,
659+
Bucket,
660+
'RestoreStatus',
661+
);
662+
663+
assert.strictEqual(result.Contents.length, 1);
664+
assert.strictEqual(result.Contents[0].Key, 'super-power-object');
665+
assert.strictEqual(result.Contents[0]['x-amz-meta-department'], undefined);
666+
assert.strictEqual(result.Contents[0]['x-amz-meta-hr'], undefined);
667+
});
668+
});
552669
});
553670
});

0 commit comments

Comments
 (0)