@@ -1045,6 +1045,84 @@ public void testGetSubscopedCredsLongPrincipalName() {
10451045 CredentialVendingContext .empty ());
10461046 }
10471047
1048+ @ Test
1049+ public void testGetSubscopedCredsPrincipalNameWithInvalidCharacters () {
1050+ StsClient stsClient = Mockito .mock (StsClient .class );
1051+ String roleARN = "arn:aws:iam::012345678901:role/jdoe" ;
1052+ String externalId = "externalId" ;
1053+ // Principal name with spaces and parentheses - invalid for AWS STS
1054+ PolarisPrincipal polarisPrincipalWithInvalidChars =
1055+ PolarisPrincipal .of ("Invalid Principal (local)" , Map .of (), Set .of ());
1056+
1057+ Mockito .when (stsClient .assumeRole (Mockito .isA (AssumeRoleRequest .class )))
1058+ .thenAnswer (
1059+ invocation -> {
1060+ assertThat (invocation .getArguments ()[0 ])
1061+ .isInstanceOf (AssumeRoleRequest .class )
1062+ .asInstanceOf (InstanceOfAssertFactories .type (AssumeRoleRequest .class ))
1063+ .returns (externalId , AssumeRoleRequest ::externalId )
1064+ .returns (roleARN , AssumeRoleRequest ::roleArn )
1065+ // Spaces and parentheses should be replaced with underscores
1066+ .returns ("polaris-Invalid_Principal__local_" , AssumeRoleRequest ::roleSessionName );
1067+ return ASSUME_ROLE_RESPONSE ;
1068+ });
1069+ String warehouseDir = "s3://bucket/path/to/warehouse" ;
1070+ new AwsCredentialsStorageIntegration (
1071+ AwsStorageConfigurationInfo .builder ()
1072+ .addAllowedLocation (warehouseDir )
1073+ .roleARN (roleARN )
1074+ .externalId (externalId )
1075+ .build (),
1076+ stsClient )
1077+ .getSubscopedCreds (
1078+ PRINCIPAL_INCLUDER_REALM_CONFIG ,
1079+ true ,
1080+ Set .of (warehouseDir + "/namespace/table" ),
1081+ Set .of (warehouseDir + "/namespace/table" ),
1082+ polarisPrincipalWithInvalidChars ,
1083+ Optional .of ("/namespace/table/credentials" ),
1084+ CredentialVendingContext .empty ());
1085+ }
1086+
1087+ @ Test
1088+ public void testGetSubscopedCredsPrincipalNameWithSpecialCharacters () {
1089+ StsClient stsClient = Mockito .mock (StsClient .class );
1090+ String roleARN = "arn:aws:iam::012345678901:role/jdoe" ;
1091+ String externalId = "externalId" ;
1092+ // Principal name with slashes and colons
1093+ PolarisPrincipal polarisPrincipalWithSpecialChars =
1094+ PolarisPrincipal .of ("service/account:readonly" , Map .of (), Set .of ());
1095+
1096+ Mockito .when (stsClient .assumeRole (Mockito .isA (AssumeRoleRequest .class )))
1097+ .thenAnswer (
1098+ invocation -> {
1099+ assertThat (invocation .getArguments ()[0 ])
1100+ .isInstanceOf (AssumeRoleRequest .class )
1101+ .asInstanceOf (InstanceOfAssertFactories .type (AssumeRoleRequest .class ))
1102+ .returns (externalId , AssumeRoleRequest ::externalId )
1103+ .returns (roleARN , AssumeRoleRequest ::roleArn )
1104+ // Slashes and colons should be replaced with underscores
1105+ .returns ("polaris-service_account_readonly" , AssumeRoleRequest ::roleSessionName );
1106+ return ASSUME_ROLE_RESPONSE ;
1107+ });
1108+ String warehouseDir = "s3://bucket/path/to/warehouse" ;
1109+ new AwsCredentialsStorageIntegration (
1110+ AwsStorageConfigurationInfo .builder ()
1111+ .addAllowedLocation (warehouseDir )
1112+ .roleARN (roleARN )
1113+ .externalId (externalId )
1114+ .build (),
1115+ stsClient )
1116+ .getSubscopedCreds (
1117+ PRINCIPAL_INCLUDER_REALM_CONFIG ,
1118+ true ,
1119+ Set .of (warehouseDir + "/namespace/table" ),
1120+ Set .of (warehouseDir + "/namespace/table" ),
1121+ polarisPrincipalWithSpecialChars ,
1122+ Optional .of ("/namespace/table/credentials" ),
1123+ CredentialVendingContext .empty ());
1124+ }
1125+
10481126 private static @ Nonnull String s3Arn (String partition , String bucket , String keyPrefix ) {
10491127 String bucketArn = "arn:" + partition + ":s3:::" + bucket ;
10501128 if (keyPrefix == null ) {
0 commit comments