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,22 @@ 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' ) ;
26+ const { PutPublicAccessBlockCommand } = require ( '@aws-sdk/client-s3' ) ;
2427const 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+
2638const 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
889901mocha . 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