Skip to content

Commit a591aeb

Browse files
authored
iam: implement getInlinePolicyDetails API for AWS Substrate (#233)
1 parent e507afb commit a591aeb

File tree

2 files changed

+129
-13
lines changed

2 files changed

+129
-13
lines changed

iam/iam-aws/src/main/java/com/salesforce/multicloudj/iam/aws/AwsIam.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@
2727
import software.amazon.awssdk.services.iam.model.EntityAlreadyExistsException;
2828
import software.amazon.awssdk.services.iam.model.GetRoleRequest;
2929
import software.amazon.awssdk.services.iam.model.GetRoleResponse;
30+
import software.amazon.awssdk.services.iam.model.GetRolePolicyRequest;
31+
import software.amazon.awssdk.services.iam.model.GetRolePolicyResponse;
3032
import software.amazon.awssdk.services.iam.model.Role;
3133
import software.amazon.awssdk.services.iam.model.UpdateAssumeRolePolicyRequest;
3234
import software.amazon.awssdk.services.iam.model.UpdateRoleRequest;
3335

3436
import java.net.URLDecoder;
3537
import java.nio.charset.StandardCharsets;
3638

39+
import java.net.URLDecoder;
40+
import java.nio.charset.StandardCharsets;
3741
import java.util.ArrayList;
3842
import java.util.LinkedHashMap;
3943
import java.util.List;
@@ -329,9 +333,37 @@ protected void doAttachInlinePolicy(PolicyDocument policyDocument, String tenant
329333
throw new UnsupportedOperationException();
330334
}
331335

336+
/**
337+
* Get inline policy document attached to an IAM role.
338+
*
339+
* @param identityName the IAM role name
340+
* @param policyName the name of the inline policy to retrieve.
341+
* @param roleName the IAM role name that has the inline policy attached.
342+
* @param tenantId the AWS Account ID.
343+
* @param region the AWS region for the IAM client.
344+
*
345+
* @return the inline policy document as a JSON string
346+
*
347+
* @throws software.amazon.awssdk.services.iam.model.NoSuchEntityException if the role or policy does not exist
348+
* @throws software.amazon.awssdk.services.iam.model.IamException for other IAM service errors
349+
*/
332350
@Override
333351
protected String doGetInlinePolicyDetails(String identityName, String policyName, String roleName, String tenantId, String region) {
334-
throw new UnsupportedOperationException();
352+
if (StringUtils.isBlank(identityName)) {
353+
throw new InvalidArgumentException("identityName is required for AWS IAM");
354+
}
355+
356+
if (StringUtils.isBlank(policyName)) {
357+
throw new InvalidArgumentException("policyName is required for AWS IAM");
358+
}
359+
360+
IamClient client = this.iamClient;
361+
GetRolePolicyRequest request = GetRolePolicyRequest.builder()
362+
.roleName(identityName)
363+
.policyName(policyName)
364+
.build();
365+
GetRolePolicyResponse response = client.getRolePolicy(request);
366+
return URLDecoder.decode(response.policyDocument(), StandardCharsets.UTF_8);
335367
}
336368

337369
@Override

iam/iam-aws/src/test/java/com/salesforce/multicloudj/iam/aws/AwsIamTest.java

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
import software.amazon.awssdk.services.iam.model.EntityAlreadyExistsException;
1919
import software.amazon.awssdk.services.iam.model.GetRoleRequest;
2020
import software.amazon.awssdk.services.iam.model.GetRoleResponse;
21+
import software.amazon.awssdk.services.iam.model.GetRolePolicyRequest;
22+
import software.amazon.awssdk.services.iam.model.GetRolePolicyResponse;
23+
import software.amazon.awssdk.services.iam.model.NoSuchEntityException;
2124
import software.amazon.awssdk.services.iam.model.Role;
2225
import software.amazon.awssdk.services.iam.model.UpdateAssumeRolePolicyRequest;
2326
import software.amazon.awssdk.services.iam.model.UpdateAssumeRolePolicyResponse;
@@ -49,6 +52,8 @@ public class AwsIamTest {
4952
private static final String TEST_TENANT_ID = "123456789012";
5053
private static final String TEST_REGION = "us-west-2";
5154
private static final String TEST_ROLE_ARN = "arn:aws:iam::123456789012:role/TestRole";
55+
private static final String TEST_POLICY_NAME = "TestPolicy";
56+
private static final String TEST_POLICY_DOCUMENT = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"s3:GetObject\",\"Resource\":\"*\"}]}";
5257
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
5358

5459
@Mock
@@ -157,16 +162,10 @@ void testCreateIdentityWithoutTrustConfigDefaultsToSameAccountRoot() throws Exce
157162

158163
JsonNode stmt = doc.at("/Statement/0");
159164
assertFalse(stmt.isMissingNode(), "Statement should not be missing");
160-
JsonNode effect = stmt.at("/Effect");
161-
assertFalse(effect.isMissingNode(), "Effect should not be missing");
162-
assertEquals("Allow", effect.asText());
163-
JsonNode action = stmt.at("/Action");
164-
assertFalse(action.isMissingNode(), "Action should not be missing");
165-
assertEquals("sts:AssumeRole", action.asText());
166-
JsonNode principal = stmt.at("/Principal/AWS");
167-
assertFalse(principal.isMissingNode(), "Principal should not be missing");
165+
assertEquals("Allow", stmt.at("/Effect").asText());
166+
assertEquals("sts:AssumeRole", stmt.at("/Action").asText());
168167
assertEquals("arn:aws:iam::" + TEST_TENANT_ID + ":root",
169-
principal.asText());
168+
stmt.at("/Principal/AWS").asText());
170169
}
171170

172171
@Test
@@ -443,7 +442,7 @@ void testCreateIdentityAlreadyExistsUpdatesTrustPolicyWhenDifferent() throws Exc
443442

444443
assertEquals(TEST_ROLE_ARN, result);
445444

446-
ArgumentCaptor<UpdateAssumeRolePolicyRequest> updatePolicyCaptor =
445+
ArgumentCaptor<UpdateAssumeRolePolicyRequest> updatePolicyCaptor =
447446
ArgumentCaptor.forClass(UpdateAssumeRolePolicyRequest.class);
448447
verify(mockIamClient, times(1)).updateAssumeRolePolicy(updatePolicyCaptor.capture());
449448
assertEquals(TEST_ROLE_NAME, updatePolicyCaptor.getValue().roleName());
@@ -535,15 +534,100 @@ void testCreateIdentityAlreadyExistsUpdatesMultipleAttributesWhenDifferent() thr
535534
assertEquals(newDescription, updateCaptor.getValue().description());
536535
assertEquals(7200, updateCaptor.getValue().maxSessionDuration());
537536

538-
ArgumentCaptor<UpdateAssumeRolePolicyRequest> updatePolicyCaptor =
537+
ArgumentCaptor<UpdateAssumeRolePolicyRequest> updatePolicyCaptor =
539538
ArgumentCaptor.forClass(UpdateAssumeRolePolicyRequest.class);
540539
verify(mockIamClient, times(1)).updateAssumeRolePolicy(updatePolicyCaptor.capture());
541540
assertEquals(TEST_ROLE_NAME, updatePolicyCaptor.getValue().roleName());
542541
}
543542

544543
private String buildDefaultAssumeRolePolicy() {
545544
return "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\"," +
546-
"\"Action\":\"sts:AssumeRole\",\"Principal\":{\"AWS\":\"arn:aws:iam::" +
545+
"\"Action\":\"sts:AssumeRole\",\"Principal\":{\"AWS\":\"arn:aws:iam::" +
547546
TEST_TENANT_ID + ":root\"}}]}";
548547
}
548+
549+
@Test
550+
void testGetInlinePolicyDetailsReturnsDocument() {
551+
when(mockIamClient.getRolePolicy(any(GetRolePolicyRequest.class)))
552+
.thenReturn(GetRolePolicyResponse.builder()
553+
.policyDocument(TEST_POLICY_DOCUMENT)
554+
.build());
555+
556+
String result = awsIam.getInlinePolicyDetails(
557+
TEST_ROLE_NAME,
558+
TEST_POLICY_NAME,
559+
TEST_ROLE_NAME,
560+
TEST_TENANT_ID,
561+
TEST_REGION);
562+
563+
assertEquals(TEST_POLICY_DOCUMENT, result);
564+
565+
ArgumentCaptor<GetRolePolicyRequest> captor = ArgumentCaptor.forClass(GetRolePolicyRequest.class);
566+
verify(mockIamClient, times(1)).getRolePolicy(captor.capture());
567+
assertEquals(TEST_ROLE_NAME, captor.getValue().roleName());
568+
assertEquals(TEST_POLICY_NAME, captor.getValue().policyName());
569+
}
570+
571+
@Test
572+
void testGetInlinePolicyDetailsWithNoSuchEntityException() {
573+
when(mockIamClient.getRolePolicy(any(GetRolePolicyRequest.class)))
574+
.thenThrow(NoSuchEntityException.builder()
575+
.message("Policy not found")
576+
.build());
577+
578+
assertThrows(NoSuchEntityException.class, () ->
579+
awsIam.getInlinePolicyDetails(
580+
TEST_ROLE_NAME,
581+
TEST_POLICY_NAME,
582+
TEST_ROLE_NAME,
583+
TEST_TENANT_ID,
584+
TEST_REGION)
585+
);
586+
587+
ArgumentCaptor<GetRolePolicyRequest> captor = ArgumentCaptor.forClass(GetRolePolicyRequest.class);
588+
verify(mockIamClient, times(1)).getRolePolicy(captor.capture());
589+
assertEquals(TEST_ROLE_NAME, captor.getValue().roleName());
590+
assertEquals(TEST_POLICY_NAME, captor.getValue().policyName());
591+
}
592+
593+
@Test
594+
void testGetInlinePolicyDetailsVerifiesParameters() {
595+
when(mockIamClient.getRolePolicy(any(GetRolePolicyRequest.class)))
596+
.thenReturn(GetRolePolicyResponse.builder()
597+
.policyDocument(TEST_POLICY_DOCUMENT)
598+
.build());
599+
600+
awsIam.getInlinePolicyDetails(
601+
TEST_ROLE_NAME,
602+
TEST_POLICY_NAME,
603+
TEST_ROLE_NAME,
604+
TEST_TENANT_ID,
605+
TEST_REGION);
606+
607+
ArgumentCaptor<GetRolePolicyRequest> captor = ArgumentCaptor.forClass(GetRolePolicyRequest.class);
608+
verify(mockIamClient).getRolePolicy(captor.capture());
609+
610+
GetRolePolicyRequest capturedRequest = captor.getValue();
611+
assertEquals(TEST_ROLE_NAME, capturedRequest.roleName());
612+
assertEquals(TEST_POLICY_NAME, capturedRequest.policyName());
613+
}
614+
615+
@Test
616+
void testGetInlinePolicyDetailsThrowsGenericException() {
617+
RuntimeException genericException = new RuntimeException("Service error");
618+
619+
when(mockIamClient.getRolePolicy(any(GetRolePolicyRequest.class)))
620+
.thenThrow(genericException);
621+
622+
RuntimeException exception = assertThrows(RuntimeException.class, () ->
623+
awsIam.getInlinePolicyDetails(
624+
TEST_ROLE_NAME,
625+
TEST_POLICY_NAME,
626+
TEST_ROLE_NAME,
627+
TEST_TENANT_ID,
628+
TEST_REGION)
629+
);
630+
631+
assertEquals(genericException, exception);
632+
}
549633
}

0 commit comments

Comments
 (0)