55const { require_coretest, is_nc_coretest, generate_iam_client,
66 generate_s3_client, generate_sts_client, err_code } = require ( '../../../system_tests/test_utils' ) ;
77const coretest = require_coretest ( ) ;
8- coretest . setup ( ) ;
8+
99const path = require ( 'path' ) ;
1010const fs = require ( 'fs' ) ;
1111const mocha = require ( 'mocha' ) ;
@@ -19,10 +19,19 @@ const config = require('../../../../../config');
1919const ldap_client = require ( '../../../../util/ldap_client' ) ;
2020const { S3Error } = require ( '../../../../endpoint/s3/s3_errors' ) ;
2121const { 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' ) ;
2325const { AssumeRoleCommand, AssumeRoleWithWebIdentityCommand } = require ( '@aws-sdk/client-sts' ) ;
2426const defualt_expiry_seconds = Math . ceil ( config . STS_DEFAULT_SESSION_TOKEN_EXPIRY_MS / 1000 ) ;
2527
28+
29+ let setup_options ;
30+ if ( is_nc_coretest ) {
31+ setup_options = { should_run_iam : true , https_port_iam : 7005 , debug : 5 } ;
32+ }
33+ coretest . setup ( setup_options ) ;
34+
2635const errors = {
2736 expired_token_s3 : {
2837 code : S3Error . ExpiredToken . code ,
@@ -887,8 +896,6 @@ mocha.describe('Assume role with web indentity tests', function() {
887896} ) ;
888897
889898mocha . describe ( 'STS assumed-role IAM policy authorization tests' , function ( ) {
890- if ( is_nc_coretest ) this . skip ( ) ; // eslint-disable-line no-invalid-this
891-
892899 const { rpc_client } = coretest ;
893900 const owner_email = 'role-authz-owner' ;
894901 const assumer_email = 'role-authz-assumer' ;
@@ -905,12 +912,20 @@ mocha.describe('STS assumed-role IAM policy authorization tests', function() {
905912 self . timeout ( 60000 ) ;
906913
907914 for ( const account of accounts ) {
908- account . access_keys = ( await rpc_client . account . create_account ( {
915+ let create_account_param = {
909916 has_login : false ,
910917 s3_access : true ,
911918 name : account . email ,
912919 email : account . email ,
913- } ) ) . access_keys ;
920+ } ;
921+ if ( is_nc_coretest ) {
922+ create_account_param . nsfs_account_config = {
923+ uid : process . getuid ( ) ,
924+ gid : process . getgid ( ) ,
925+ new_buckets_path : coretest . NC_CORETEST_STORAGE_PATH ,
926+ } ;
927+ }
928+ account . access_keys = ( await rpc_client . account . create_account ( create_account_param ) ) . access_keys ;
914929 const access_key = account . access_keys [ 0 ] . access_key . unwrap ( ) ;
915930 const secret_key = account . access_keys [ 0 ] . secret_key . unwrap ( ) ;
916931 account . sts_client = generate_sts_client ( access_key , secret_key , coretest . get_https_address_sts ( ) ) ;
@@ -1013,3 +1028,188 @@ mocha.describe('STS assumed-role IAM policy authorization tests', function() {
10131028 assert . equal ( response . $metadata . httpStatusCode , 200 ) ;
10141029 } ) ;
10151030} ) ;
1031+
1032+ mocha . describe ( 'Cloudera RAZ-style S3 role test' , function ( ) {
1033+ const { rpc_client } = coretest ;
1034+
1035+ // account that owns the role and the bucket
1036+ const owner_email = 'raz-role-owner' ;
1037+ // IAM user (sub-user) under owner_email that will assume the role
1038+ const iam_username = 'raz-iam-user' ;
1039+ const role_name = 'RazS3Role' ;
1040+ const policy_name = 'RazS3InlinePolicy' ;
1041+ const bucket_name = 'raz-test-bucket' ;
1042+ const object_key = 'dummy-object.txt' ;
1043+
1044+ const owner = { email : owner_email } ;
1045+ let owner_account_info ;
1046+ let iam_user_arn ;
1047+ let iam_user_access_key_id ;
1048+ let iam_user_secret_key ;
1049+
1050+ const inline_policy = {
1051+ Version : '2012-10-17' ,
1052+ Statement : [
1053+ {
1054+ Effect : 'Allow' ,
1055+ Action : [ 's3:GetBucketLocation' , 's3:ListBucket' ] ,
1056+ Resource : [ `arn:aws:s3:::${ bucket_name } ` ] ,
1057+ } ,
1058+ {
1059+ Effect : 'Allow' ,
1060+ Action : [ 's3:GetObject' , 's3:PutObject' , 's3:DeleteObject' ] ,
1061+ Resource : [ `arn:aws:s3:::${ bucket_name } /*` ] ,
1062+ } ,
1063+ ] ,
1064+ } ;
1065+
1066+ mocha . after ( async function ( ) {
1067+ const self = this ; // eslint-disable-line no-invalid-this
1068+ self . timeout ( 60000 ) ;
1069+
1070+ await owner . s3_client . deleteObject ( { Bucket : bucket_name , Key : object_key } ) ;
1071+ await owner . s3_client . deleteBucket ( { Bucket : bucket_name } ) ;
1072+ await owner . iam_client . send ( new DeleteRolePolicyCommand ( { RoleName : role_name , PolicyName : policy_name } ) ) ;
1073+ await owner . iam_client . send ( new DeleteRoleCommand ( { RoleName : role_name } ) ) ;
1074+ await owner . iam_client . send ( new DeleteAccessKeyCommand ( {
1075+ UserName : iam_username ,
1076+ AccessKeyId : iam_user_access_key_id ,
1077+ } ) ) ;
1078+ await owner . iam_client . send ( new DeleteUserCommand ( { UserName : iam_username } ) ) ;
1079+ await rpc_client . account . delete_account ( { email : owner_email } ) ;
1080+ } ) ;
1081+
1082+ mocha . before ( async function ( ) {
1083+ const self = this ; // eslint-disable-line no-invalid-this
1084+ self . timeout ( 60000 ) ;
1085+
1086+ // 1. Create the owner account (NooBaa account that owns the role and bucket)
1087+ const create_account_param = {
1088+ has_login : false ,
1089+ s3_access : true ,
1090+ name : owner_email ,
1091+ email : owner_email ,
1092+ } ;
1093+ if ( is_nc_coretest ) {
1094+ create_account_param . nsfs_account_config = {
1095+ uid : process . getuid ( ) ,
1096+ gid : process . getgid ( ) ,
1097+ new_buckets_path : coretest . NC_CORETEST_STORAGE_PATH ,
1098+ } ;
1099+ }
1100+ owner . access_keys = ( await rpc_client . account . create_account ( create_account_param ) ) . access_keys ;
1101+ owner_account_info = await rpc_client . account . read_account ( { email : owner_email } ) ;
1102+
1103+ const access_key = owner . access_keys [ 0 ] . access_key . unwrap ( ) ;
1104+ const secret_key = owner . access_keys [ 0 ] . secret_key . unwrap ( ) ;
1105+ owner . iam_client = generate_iam_client ( access_key , secret_key , coretest . get_https_address_iam ( ) ) ;
1106+ owner . s3_client = generate_s3_client ( access_key , secret_key , coretest . get_http_address ( ) ) ;
1107+
1108+ // 2. Create an IAM user under the owner account
1109+ const create_user_resp = await owner . iam_client . send ( new CreateUserCommand ( { UserName : iam_username } ) ) ;
1110+ iam_user_arn = create_user_resp . User . Arn ;
1111+
1112+ // 3. Create access keys for the IAM user (step 2 of the test scenario)
1113+ const create_key_resp = await owner . iam_client . send ( new CreateAccessKeyCommand ( { UserName : iam_username } ) ) ;
1114+ iam_user_access_key_id = create_key_resp . AccessKey . AccessKeyId ;
1115+ iam_user_secret_key = create_key_resp . AccessKey . SecretAccessKey ;
1116+
1117+ // 4. Create the bucket (step 3 of the test scenario)
1118+ await owner . s3_client . createBucket ( { Bucket : bucket_name } ) ;
1119+
1120+ } ) ;
1121+
1122+ mocha . it ( 'cloudera req with role' , async function ( ) {
1123+
1124+ // 5. Create the role with a trust policy allowing the IAM user to assume it (step 4)
1125+ const trust_policy = {
1126+ Version : '2012-10-17' ,
1127+ Statement : [ {
1128+ Effect : 'Allow' ,
1129+ Principal : { AWS : [ iam_user_arn ] } ,
1130+ Action : [ 'sts:AssumeRole' ] ,
1131+ } ] ,
1132+ } ;
1133+ await owner . iam_client . send ( new CreateRoleCommand ( {
1134+ RoleName : role_name ,
1135+ AssumeRolePolicyDocument : JSON . stringify ( trust_policy ) ,
1136+ } ) ) ;
1137+
1138+ // 6. Put the inline role policy granting the Cloudera RAZ-required S3 permissions
1139+ // on the created bucket (step 5 of the test scenario).
1140+ // Mirrors the "Storage prerequisites" S3 role policy from the Cloudera RAZ document:
1141+ // GetBucketLocation, ListBucket on the bucket; GetObject, PutObject, DeleteObject on objects.
1142+ await owner . iam_client . send ( new PutRolePolicyCommand ( {
1143+ RoleName : role_name ,
1144+ PolicyName : policy_name ,
1145+ PolicyDocument : JSON . stringify ( inline_policy ) ,
1146+ } ) ) ;
1147+
1148+ // 7. With the IAM user's credentials, assume the role (step 6 of the test scenario)
1149+ const owner_account_id = owner_account_info . _id . toString ( ) ;
1150+ const iam_user_sts = generate_sts_client (
1151+ iam_user_access_key_id ,
1152+ iam_user_secret_key ,
1153+ coretest . get_https_address_sts ( )
1154+ ) ;
1155+
1156+ const assume_role_params = {
1157+ RoleArn : `arn:aws:sts::${ owner_account_id } :role/${ role_name } ` ,
1158+ RoleSessionName : 'raz-test-session' ,
1159+ } ;
1160+ const assume_resp = await iam_user_sts . send ( new AssumeRoleCommand ( assume_role_params ) ) ;
1161+ const owner_key = owner . access_keys [ 0 ] . access_key . unwrap ( ) ;
1162+ const creds = validate_assume_role_response (
1163+ assume_resp ,
1164+ `arn:aws:sts::${ owner_account_id } :assumed-role/${ role_name } /${ assume_role_params . RoleSessionName } ` ,
1165+ `${ owner_account_id } :${ assume_role_params . RoleSessionName } ` ,
1166+ owner_key ,
1167+ defualt_expiry_seconds
1168+ ) ;
1169+
1170+ // 8. With the temporary credentials, put a dummy object in the bucket (step 7)
1171+ const temp_s3 = generate_s3_client (
1172+ creds . access_key ,
1173+ creds . secret_key ,
1174+ coretest . get_http_address ( ) ,
1175+ creds . session_token
1176+ ) ;
1177+ const put_resp = await temp_s3 . putObject ( {
1178+ Bucket : bucket_name ,
1179+ Key : object_key ,
1180+ Body : 'dummy content for raz test' ,
1181+ } ) ;
1182+ assert . equal ( put_resp . $metadata . httpStatusCode , 200 ) ;
1183+ } ) ;
1184+
1185+ mocha . it ( 'cloudera req with inline user policy' , async function ( ) {
1186+ const user_policy_name = 'RazS3UserInlinePolicy' ;
1187+
1188+ // Put the same Cloudera RAZ-required S3 permissions as an inline user policy
1189+ // directly on the IAM user — no role or assume-role involved.
1190+ await owner . iam_client . send ( new PutUserPolicyCommand ( {
1191+ UserName : iam_username ,
1192+ PolicyName : user_policy_name ,
1193+ PolicyDocument : JSON . stringify ( inline_policy ) ,
1194+ } ) ) ;
1195+
1196+ // Upload a dummy object directly with the IAM user's permanent credentials.
1197+ const iam_user_s3 = generate_s3_client (
1198+ iam_user_access_key_id ,
1199+ iam_user_secret_key ,
1200+ coretest . get_http_address ( )
1201+ ) ;
1202+ const put_resp = await iam_user_s3 . putObject ( {
1203+ Bucket : bucket_name ,
1204+ Key : object_key ,
1205+ Body : 'dummy content for raz user policy test' ,
1206+ } ) ;
1207+ assert . equal ( put_resp . $metadata . httpStatusCode , 200 ) ;
1208+
1209+ // Clean up the inline user policy so after() doesn't leave stale state.
1210+ await owner . iam_client . send ( new DeleteUserPolicyCommand ( {
1211+ UserName : iam_username ,
1212+ PolicyName : user_policy_name ,
1213+ } ) ) ;
1214+ } ) ;
1215+ } ) ;
0 commit comments