Skip to content

Commit eeda91f

Browse files
committed
nc - iam - enable inline, cloudera test
Signed-off-by: Amit Prinz Setter <alphaprinz@gmail.com>
1 parent 6db404d commit eeda91f

3 files changed

Lines changed: 243 additions & 11 deletions

File tree

src/endpoint/iam/iam_utils.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,9 +1408,6 @@ async function authorize_request_iam_policy_impl(req, method, bucket_name, servi
14081408
return deny_result;
14091409
}
14101410
if (iam_policies.length === 0) {
1411-
// TODO: remove NC empty-policy allow when PutRolePolicy (Phase 2) is implemented
1412-
// NC: IAM user / role inline policies are Phase 2; allow until PutRolePolicy exists
1413-
if (req.object_sdk.nsfs_config_root && (is_iam_user || is_assumed_role_session)) return true;
14141411
dbg.error('authorize_request_iam_policy:', iam_identity, 'has no inline policies configured');
14151412
return deny_result;
14161413
}

src/sdk/accountspace_fs.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,8 @@ class AccountSpaceFS {
902902
dbg.error(`AccountSpaceFS.${action} error`, err);
903903
throw native_fs_utils.translate_error_codes(err, native_fs_utils.entity_enum.ROLE);
904904
}
905-
const policy_index = this._check_iam_policy_exists(action, iam_role_policies, params.policy_name, 'role');
905+
const policy_index = this._check_iam_policy_exists(action, iam_role_policies, params.policy_name,
906+
native_fs_utils.entity_enum.ROLE.toLowerCase());
906907
return {
907908
role_name: params.role_name,
908909
policy_name: params.policy_name,
@@ -924,7 +925,8 @@ class AccountSpaceFS {
924925
{role_name: params.role_name}, native_fs_utils.entity_enum.ROLE);
925926
const { owner_account_id, role_data } = await this._check_if_role_exists(params, requesting_account);
926927
const iam_role_policies = [...(role_data.iam_inline_policies || [])];
927-
const policy_index = this._check_iam_policy_exists(action, iam_role_policies, params.policy_name);
928+
const policy_index = this._check_iam_policy_exists(action, iam_role_policies, params.policy_name,
929+
native_fs_utils.entity_enum.ROLE.toLowerCase());
928930
iam_role_policies.splice(policy_index, 1);
929931
role_data.iam_inline_policies = iam_role_policies;
930932
await this.config_fs.update_role_config_file(role_data);

src/test/integration_tests/api/sts/test_sts.js

Lines changed: 239 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
const { require_coretest, is_nc_coretest, generate_iam_client,
66
generate_s3_client, generate_sts_client, err_code } = require('../../../system_tests/test_utils');
77
const coretest = require_coretest();
8-
coretest.setup();
8+
99
const path = require('path');
1010
const fs = require('fs');
1111
const mocha = require('mocha');
@@ -19,10 +19,22 @@ const config = require('../../../../../config');
1919
const ldap_client = require('../../../../util/ldap_client');
2020
const { S3Error } = require('../../../../endpoint/s3/s3_errors');
2121
const { CreateRoleCommand, DeleteRoleCommand, DeleteRolePolicyCommand,
22-
PutRolePolicyCommand, UpdateAssumeRolePolicyCommand } = require('@aws-sdk/client-iam');
22+
PutRolePolicyCommand, UpdateAssumeRolePolicyCommand,
23+
CreateUserCommand, CreateAccessKeyCommand, DeleteAccessKeyCommand, DeleteUserCommand,
24+
PutUserPolicyCommand, DeleteUserPolicyCommand} = require('@aws-sdk/client-iam');
2325
const { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand } = require('@aws-sdk/client-sts');
26+
const { PutPublicAccessBlockCommand } = require('@aws-sdk/client-s3');
2427
const defualt_expiry_seconds = Math.ceil(config.STS_DEFAULT_SESSION_TOKEN_EXPIRY_MS / 1000);
2528

29+
30+
let setup_options;
31+
if (is_nc_coretest) {
32+
setup_options = { should_run_iam: true, https_port_iam: 7005, debug: 5 };
33+
} else {
34+
setup_options = { pools_to_create: [coretest.POOL_LIST[1]] };
35+
}
36+
coretest.setup(setup_options);
37+
2638
const errors = {
2739
expired_token_s3: {
2840
code: S3Error.ExpiredToken.code,
@@ -887,8 +899,6 @@ mocha.describe('Assume role with web indentity tests', function() {
887899
});
888900

889901
mocha.describe('STS assumed-role IAM policy authorization tests', function() {
890-
if (is_nc_coretest) this.skip(); // eslint-disable-line no-invalid-this
891-
892902
const { rpc_client } = coretest;
893903
const owner_email = 'role-authz-owner';
894904
const assumer_email = 'role-authz-assumer';
@@ -905,12 +915,20 @@ mocha.describe('STS assumed-role IAM policy authorization tests', function() {
905915
self.timeout(60000);
906916

907917
for (const account of accounts) {
908-
account.access_keys = (await rpc_client.account.create_account({
918+
const create_account_param = {
909919
has_login: false,
910920
s3_access: true,
911921
name: account.email,
912922
email: account.email,
913-
})).access_keys;
923+
};
924+
if (is_nc_coretest) {
925+
create_account_param.nsfs_account_config = {
926+
uid: process.getuid(),
927+
gid: process.getgid(),
928+
new_buckets_path: coretest.NC_CORETEST_STORAGE_PATH,
929+
};
930+
}
931+
account.access_keys = (await rpc_client.account.create_account(create_account_param)).access_keys;
914932
const access_key = account.access_keys[0].access_key.unwrap();
915933
const secret_key = account.access_keys[0].secret_key.unwrap();
916934
account.sts_client = generate_sts_client(access_key, secret_key, coretest.get_https_address_sts());
@@ -1013,3 +1031,218 @@ mocha.describe('STS assumed-role IAM policy authorization tests', function() {
10131031
assert.equal(response.$metadata.httpStatusCode, 200);
10141032
});
10151033
});
1034+
1035+
mocha.describe('Cloudera RAZ-style S3 role test', function() {
1036+
const { rpc_client } = coretest;
1037+
1038+
// account that owns the role and the bucket
1039+
const owner_email = 'raz-role-owner';
1040+
// IAM user (sub-user) under owner_email that will assume the role
1041+
const iam_username = 'raz-iam-user';
1042+
const role_name = 'RazS3Role';
1043+
const policy_name = 'RazS3InlinePolicy';
1044+
const bucket_name = 'raz-test-bucket';
1045+
const object_key = 'dummy-object.txt';
1046+
1047+
const owner = { email: owner_email };
1048+
let owner_account_info;
1049+
let iam_user_arn;
1050+
let iam_user_access_key_id;
1051+
let iam_user_secret_key;
1052+
1053+
const inline_policy = {
1054+
Version: '2012-10-17',
1055+
Statement: [
1056+
{
1057+
"Sid": "AccessToBucket",
1058+
Effect: 'Allow',
1059+
Action: [
1060+
"s3:GetBucketAcl",
1061+
"s3:GetBucketLocation",
1062+
"s3:GetBucketVersioning",
1063+
"s3:GetEncryptionConfiguration",
1064+
"s3:ListBucket",
1065+
"s3:ListBucketMultipartUploads"
1066+
],
1067+
Resource: [`arn:aws:s3:::${bucket_name}`],
1068+
},
1069+
{
1070+
"Sid": "AccessToBucketObjects",
1071+
Effect: 'Allow',
1072+
Action: [
1073+
"s3:AbortMultipartUpload",
1074+
"s3:DeleteObject",
1075+
"s3:DeleteObjectVersion",
1076+
"s3:GetObject",
1077+
"s3:GetObjectAcl",
1078+
"s3:GetObjectVersion",
1079+
"s3:GetObjectVersionAcl",
1080+
"s3:PutObject",
1081+
"s3:ListMultipartUploadParts"
1082+
],
1083+
Resource: [`arn:aws:s3:::${bucket_name}/*`],
1084+
},
1085+
],
1086+
};
1087+
1088+
mocha.after(async function() {
1089+
const self = this; // eslint-disable-line no-invalid-this
1090+
self.timeout(60000);
1091+
1092+
await owner.s3_client.deleteObject({ Bucket: bucket_name, Key: object_key });
1093+
await owner.s3_client.deleteBucket({ Bucket: bucket_name });
1094+
await owner.iam_client.send(new DeleteAccessKeyCommand({
1095+
UserName: iam_username,
1096+
AccessKeyId: iam_user_access_key_id,
1097+
}));
1098+
await owner.iam_client.send(new DeleteUserCommand({ UserName: iam_username }));
1099+
await rpc_client.account.delete_account({ email: owner_email });
1100+
});
1101+
1102+
mocha.before(async function() {
1103+
const self = this; // eslint-disable-line no-invalid-this
1104+
self.timeout(60000);
1105+
1106+
// 1. Create the owner account (NooBaa account that owns the role and bucket)
1107+
const create_account_param = {
1108+
has_login: false,
1109+
s3_access: true,
1110+
name: owner_email,
1111+
email: owner_email,
1112+
};
1113+
if (is_nc_coretest) {
1114+
create_account_param.nsfs_account_config = {
1115+
uid: process.getuid(),
1116+
gid: process.getgid(),
1117+
new_buckets_path: coretest.NC_CORETEST_STORAGE_PATH,
1118+
};
1119+
} else {
1120+
create_account_param.default_resource = coretest.POOL_LIST[1].name;
1121+
}
1122+
owner.access_keys = (await rpc_client.account.create_account(create_account_param)).access_keys;
1123+
owner_account_info = await rpc_client.account.read_account({ email: owner_email });
1124+
1125+
const access_key = owner.access_keys[0].access_key.unwrap();
1126+
const secret_key = owner.access_keys[0].secret_key.unwrap();
1127+
owner.iam_client = generate_iam_client(access_key, secret_key, coretest.get_https_address_iam());
1128+
owner.s3_client = generate_s3_client(access_key, secret_key, coretest.get_http_address());
1129+
1130+
// 2. Create an IAM user under the owner account
1131+
const create_user_resp = await owner.iam_client.send(new CreateUserCommand({ UserName: iam_username }));
1132+
iam_user_arn = create_user_resp.User.Arn;
1133+
1134+
// 3. Create access keys for the IAM user
1135+
const create_key_resp = await owner.iam_client.send(new CreateAccessKeyCommand({ UserName: iam_username }));
1136+
iam_user_access_key_id = create_key_resp.AccessKey.AccessKeyId;
1137+
iam_user_secret_key = create_key_resp.AccessKey.SecretAccessKey;
1138+
1139+
// 4. Create the bucket
1140+
await owner.s3_client.createBucket({ Bucket: bucket_name });
1141+
await owner.s3_client.send(new PutPublicAccessBlockCommand({
1142+
Bucket: bucket_name,
1143+
PublicAccessBlockConfiguration: {
1144+
BlockPublicPolicy: true,
1145+
RestrictPublicBuckets: true,
1146+
},
1147+
}));
1148+
});
1149+
1150+
mocha.it('cloudera req with inline user policy', async function() {
1151+
const self = this; // eslint-disable-line no-invalid-this
1152+
self.timeout(60000);
1153+
const user_policy_name = 'RazS3UserInlinePolicy';
1154+
1155+
// Put the same Cloudera RAZ-required S3 permissions as an inline user policy
1156+
// directly on the IAM user — no role or assume-role involved.
1157+
await owner.iam_client.send(new PutUserPolicyCommand({
1158+
UserName: iam_username,
1159+
PolicyName: user_policy_name,
1160+
PolicyDocument: JSON.stringify(inline_policy),
1161+
}));
1162+
1163+
// Upload a dummy object directly with the IAM user's permanent credentials.
1164+
const iam_user_s3 = generate_s3_client(
1165+
iam_user_access_key_id,
1166+
iam_user_secret_key,
1167+
coretest.get_http_address()
1168+
);
1169+
const put_resp = await iam_user_s3.putObject({
1170+
Bucket: bucket_name,
1171+
Key: object_key,
1172+
Body: 'dummy content for raz user policy test',
1173+
});
1174+
assert.equal(put_resp.$metadata.httpStatusCode, 200);
1175+
1176+
await owner.iam_client.send(new DeleteUserPolicyCommand({
1177+
UserName: iam_username,
1178+
PolicyName: user_policy_name,
1179+
}));
1180+
});
1181+
1182+
mocha.it('cloudera req with role', async function() {
1183+
const self = this; // eslint-disable-line no-invalid-this
1184+
self.timeout(60000);
1185+
// 5. Create the role with a trust policy allowing the IAM user to assume it
1186+
const trust_policy = {
1187+
Version: '2012-10-17',
1188+
Statement: [{
1189+
Effect: 'Allow',
1190+
Principal: { AWS: [iam_user_arn] },
1191+
Action: ['sts:AssumeRole'],
1192+
}],
1193+
};
1194+
await owner.iam_client.send(new CreateRoleCommand({
1195+
RoleName: role_name,
1196+
AssumeRolePolicyDocument: JSON.stringify(trust_policy),
1197+
}));
1198+
1199+
// 6. Put the inline role policy granting the Cloudera RAZ-required S3 permissions
1200+
// on the created bucket
1201+
// Mirrors the "Storage prerequisites" S3 role policy from the Cloudera RAZ document:
1202+
// GetBucketLocation, ListBucket on the bucket; GetObject, PutObject, DeleteObject on objects.
1203+
await owner.iam_client.send(new PutRolePolicyCommand({
1204+
RoleName: role_name,
1205+
PolicyName: policy_name,
1206+
PolicyDocument: JSON.stringify(inline_policy),
1207+
}));
1208+
1209+
// 7. With the IAM user's credentials, assume the role
1210+
const owner_account_id = owner_account_info._id.toString();
1211+
const iam_user_sts = generate_sts_client(
1212+
iam_user_access_key_id,
1213+
iam_user_secret_key,
1214+
coretest.get_https_address_sts()
1215+
);
1216+
1217+
const assume_role_params = {
1218+
RoleArn: `arn:aws:sts::${owner_account_id}:role/${role_name}`,
1219+
RoleSessionName: 'raz-test-session',
1220+
};
1221+
const assume_resp = await iam_user_sts.send(new AssumeRoleCommand(assume_role_params));
1222+
const owner_key = owner.access_keys[0].access_key.unwrap();
1223+
const creds = validate_assume_role_response(
1224+
assume_resp,
1225+
`arn:aws:sts::${owner_account_id}:assumed-role/${role_name}/${assume_role_params.RoleSessionName}`,
1226+
`${owner_account_id}:${assume_role_params.RoleSessionName}`,
1227+
owner_key,
1228+
defualt_expiry_seconds
1229+
);
1230+
1231+
// 8. With the temporary credentials, put a dummy object in the bucket
1232+
const temp_s3 = generate_s3_client(
1233+
creds.access_key,
1234+
creds.secret_key,
1235+
coretest.get_http_address(),
1236+
creds.session_token
1237+
);
1238+
const put_resp = await temp_s3.putObject({
1239+
Bucket: bucket_name,
1240+
Key: object_key,
1241+
Body: 'dummy content for raz test',
1242+
});
1243+
assert.equal(put_resp.$metadata.httpStatusCode, 200);
1244+
1245+
await owner.iam_client.send(new DeleteRolePolicyCommand({ RoleName: role_name, PolicyName: policy_name }));
1246+
await owner.iam_client.send(new DeleteRoleCommand({ RoleName: role_name }));
1247+
});
1248+
});

0 commit comments

Comments
 (0)