input) {
+ if (input == null) {
+ return new java.util.ArrayList<>();
+ }
+ return input.stream()
+ .filter(s -> s != null && !s.trim().isEmpty())
+ .collect(java.util.stream.Collectors.toList());
+ }
+
+ /**
+ * Custom builder for Statement to handle conditions.
+ */
+ public static class StatementBuilder {
+ /**
+ * Adds a condition to the statement.
+ *
+ * @param operator the condition operator
+ * @param key the condition key
+ * @param value the condition value
+ * @return this builder
+ */
+ public StatementBuilder condition(String operator, String key, Object value) {
+ if (operator != null && key != null && value != null) {
+ if (this.conditions == null) {
+ this.conditions = new java.util.HashMap<>();
+ }
+ this.conditions.computeIfAbsent(operator, k -> new java.util.HashMap<>())
+ .put(key, value);
+ }
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/iam/iam-client/src/main/java/com/salesforce/multicloudj/iam/model/TrustConfiguration.java b/iam/iam-client/src/main/java/com/salesforce/multicloudj/iam/model/TrustConfiguration.java
new file mode 100644
index 00000000..34698062
--- /dev/null
+++ b/iam/iam-client/src/main/java/com/salesforce/multicloudj/iam/model/TrustConfiguration.java
@@ -0,0 +1,101 @@
+package com.salesforce.multicloudj.iam.model;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+
+/**
+ * Configuration for trust relationships in identity creation.
+ *
+ * This class defines which principals can assume or impersonate the identity being created,
+ * along with any conditions that must be met for the trust relationship to be valid.
+ *
+ *
Principal identifiers are accepted in their native cloud format and translated internally:
+ * - AWS: ARN format (arn:aws:iam::account:type/name)
+ * - GCP: Email format (serviceaccount@project.iam.gserviceaccount.com)
+ * - AliCloud: ACS format (acs:ram::account:type/name) or account ID
+ */
+@Getter
+public class TrustConfiguration {
+ private final List trustedPrincipals;
+ private final Map> conditions;
+
+ private TrustConfiguration(Builder builder) {
+ this.trustedPrincipals = new ArrayList<>(builder.trustedPrincipals);
+ this.conditions = new HashMap<>(builder.conditions);
+ }
+
+ /**
+ * Creates a new builder for TrustConfiguration.
+ *
+ * @return a new Builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder class for TrustConfiguration.
+ */
+ public static class Builder {
+ private final List trustedPrincipals = new ArrayList<>();
+ private final Map> conditions = new HashMap<>();
+
+ private Builder() {
+ }
+
+ /**
+ * Adds a trusted principal to the trust configuration.
+ *
+ * @param principal the principal identifier in cloud-native format (AWS ARN, GCP email, AliCloud ACS ARN or account ID)
+ * @return this Builder instance
+ */
+ public Builder addTrustedPrincipal(String principal) {
+ if (principal != null && !principal.trim().isEmpty()) {
+ this.trustedPrincipals.add(principal);
+ }
+ return this;
+ }
+
+ /**
+ * Adds multiple trusted principals to the trust configuration.
+ *
+ * @param principals the list of principal identifiers in cloud-native formats
+ * @return this Builder instance
+ */
+ public Builder addTrustedPrincipals(List principals) {
+ if (principals != null) {
+ principals.stream()
+ .filter(p -> p != null && !p.trim().isEmpty())
+ .forEach(this.trustedPrincipals::add);
+ }
+ return this;
+ }
+
+ /**
+ * Adds a condition to the trust configuration.
+ *
+ * @param operator the condition operator (e.g., "StringEquals", "IpAddress", "DateGreaterThan")
+ * @param key the condition key in cloud-native format (e.g., "aws:RequestedRegion", ""aws:SourceIp")
+ * @param value the condition value
+ * @return this Builder instance
+ */
+ public Builder addCondition(String operator, String key, Object value) {
+ if (operator != null && key != null && value != null) {
+ conditions.computeIfAbsent(operator, k -> new HashMap<>()).put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * Builds and returns a TrustConfiguration instance.
+ *
+ * @return a new TrustConfiguration instance
+ */
+ public TrustConfiguration build() {
+ return new TrustConfiguration(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/CreateOptionsTest.java b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/CreateOptionsTest.java
new file mode 100644
index 00000000..6b206dba
--- /dev/null
+++ b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/CreateOptionsTest.java
@@ -0,0 +1,35 @@
+package com.salesforce.multicloudj.iam.model;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Unit tests for CreateOptions builder pattern and functionality.
+ */
+public class CreateOptionsTest {
+
+ @Test
+ public void testCreateOptionsBuilder() {
+ CreateOptions options = CreateOptions.builder()
+ .path("/service-roles/")
+ .maxSessionDuration(3600)
+ .permissionBoundary("arn:aws:iam::123456789012:policy/PowerUserBoundary")
+ .build();
+
+ assertEquals("/service-roles/", options.getPath());
+ assertEquals(Integer.valueOf(3600), options.getMaxSessionDuration());
+ assertEquals("arn:aws:iam::123456789012:policy/PowerUserBoundary", options.getPermissionBoundary());
+ }
+
+ @Test
+ public void testCreateOptionsBuilderMinimal() {
+ CreateOptions options = CreateOptions.builder()
+ .build();
+
+ assertNull(options.getPath());
+ assertNull(options.getMaxSessionDuration());
+ assertNull(options.getPermissionBoundary());
+ }
+}
\ No newline at end of file
diff --git a/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/PolicyDocumentTest.java b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/PolicyDocumentTest.java
new file mode 100644
index 00000000..722a0eaf
--- /dev/null
+++ b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/PolicyDocumentTest.java
@@ -0,0 +1,186 @@
+package com.salesforce.multicloudj.iam.model;
+
+import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for PolicyDocument builder pattern.
+ */
+public class PolicyDocumentTest {
+
+ private static final String TEST_VERSION = "TEST_VERSION";
+
+ @Test
+ public void testPolicyDocumentBuilder() {
+ PolicyDocument policy = PolicyDocument.builder()
+ .version("2024-01-01")
+ .statement(Statement.builder()
+ .sid("StorageAccess")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .action("storage:PutObject")
+ .principal("arn:aws:iam::123456789012:user/ExampleUser")
+ .resource("storage://my-bucket/*")
+ .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .build())
+ .build();
+
+ assertEquals("2024-01-01", policy.getVersion());
+ assertEquals(1, policy.getStatements().size());
+
+ Statement statement = policy.getStatements().get(0);
+ assertEquals("StorageAccess", statement.getSid());
+ assertEquals("Allow", statement.getEffect());
+ assertEquals(Arrays.asList("storage:GetObject", "storage:PutObject"), statement.getActions());
+ assertEquals(Arrays.asList("arn:aws:iam::123456789012:user/ExampleUser"), statement.getPrincipals());
+ assertEquals(Arrays.asList("storage://my-bucket/*"), statement.getResources());
+
+ assertTrue(statement.getConditions().containsKey("StringEquals"));
+ assertEquals("us-west-2", statement.getConditions().get("StringEquals").get("aws:RequestedRegion"));
+ }
+
+ @Test
+ public void testMultipleStatements() {
+ PolicyDocument policy = PolicyDocument.builder()
+ .version(TEST_VERSION)
+ .statement(Statement.builder()
+ .sid("ReadAccess")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://my-bucket/*")
+ .build())
+ .statement(Statement.builder()
+ .sid("WriteAccess")
+ .effect("Allow")
+ .action("storage:PutObject")
+ .resource("storage://my-bucket/*")
+ .build())
+ .build();
+
+ assertEquals(2, policy.getStatements().size());
+ assertEquals("ReadAccess", policy.getStatements().get(0).getSid());
+ assertEquals("WriteAccess", policy.getStatements().get(1).getSid());
+ }
+
+ @Test
+ public void testEmptyPolicyThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ PolicyDocument.builder().build();
+ });
+ }
+
+ @Test
+ public void testMissingVersionThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ PolicyDocument.builder()
+ .statement(Statement.builder()
+ .sid("TestStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://test-bucket/*")
+ .build())
+ .build();
+ });
+ }
+
+ @Test
+ public void testStatementWithoutEffectThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ PolicyDocument.builder()
+ .version(TEST_VERSION)
+ .statement(Statement.builder()
+ .sid("TestStatement")
+ .action("storage:GetObject")
+ .build())
+ .build();
+ });
+ }
+
+ @Test
+ public void testStatementWithoutActionsThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ PolicyDocument.builder()
+ .version(TEST_VERSION)
+ .statement(Statement.builder()
+ .sid("TestStatement")
+ .effect("Allow")
+ .build())
+ .build();
+ });
+ }
+
+ @Test
+ public void testBuilderMethodsWithMultipleValues() {
+ PolicyDocument policy = PolicyDocument.builder()
+ .version(TEST_VERSION)
+ .statement(Statement.builder()
+ .sid("TestStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .action("storage:PutObject")
+ .action("storage:DeleteObject")
+ .action("storage:ListObjects")
+ .resource("storage://bucket1/*")
+ .resource("storage://bucket2/*")
+ .resource("storage://bucket3/*")
+ .resource("storage://bucket4/*")
+ .principal("principal1")
+ .principal("principal2")
+ .principal("principal3")
+ .principal("principal4")
+ .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .condition("DateGreaterThan", "aws:CurrentTime", "2024-01-01T00:00:00Z")
+ .build())
+ .build();
+
+ Statement statement = policy.getStatements().get(0);
+
+ // Test actions
+ assertEquals(4, statement.getActions().size());
+ assertTrue(statement.getActions().contains("storage:GetObject"));
+ assertTrue(statement.getActions().contains("storage:PutObject"));
+ assertTrue(statement.getActions().contains("storage:DeleteObject"));
+ assertTrue(statement.getActions().contains("storage:ListObjects"));
+
+ // Test resources
+ assertEquals(4, statement.getResources().size());
+ assertTrue(statement.getResources().contains("storage://bucket1/*"));
+ assertTrue(statement.getResources().contains("storage://bucket2/*"));
+ assertTrue(statement.getResources().contains("storage://bucket3/*"));
+ assertTrue(statement.getResources().contains("storage://bucket4/*"));
+
+ // Test principals
+ assertEquals(4, statement.getPrincipals().size());
+ assertTrue(statement.getPrincipals().contains("principal1"));
+ assertTrue(statement.getPrincipals().contains("principal2"));
+ assertTrue(statement.getPrincipals().contains("principal3"));
+ assertTrue(statement.getPrincipals().contains("principal4"));
+
+ // Test conditions
+ assertTrue(statement.getConditions().containsKey("StringEquals"));
+ assertTrue(statement.getConditions().containsKey("DateGreaterThan"));
+ }
+
+ @Test
+ public void testAddNullStatement() {
+ PolicyDocument policy = PolicyDocument.builder()
+ .version(TEST_VERSION)
+ .statement((Statement) null)
+ .statement(Statement.builder()
+ .sid("ValidStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://test-bucket/*")
+ .build())
+ .build();
+
+ assertEquals(1, policy.getStatements().size()); // Null statements are filtered out
+ assertEquals("ValidStatement", policy.getStatements().get(0).getSid());
+ }
+}
\ No newline at end of file
diff --git a/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/StatementTest.java b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/StatementTest.java
new file mode 100644
index 00000000..5aeec870
--- /dev/null
+++ b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/StatementTest.java
@@ -0,0 +1,248 @@
+package com.salesforce.multicloudj.iam.model;
+
+import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for Statement builder pattern and functionality.
+ */
+public class StatementTest {
+
+ @Test
+ public void testStatementBuilder() {
+ Statement statement = Statement.builder()
+ .sid("TestStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .action("storage:PutObject")
+ .resource("storage://my-bucket/*")
+ .principal("arn:aws:iam::123456789012:user/TestUser")
+ .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .build();
+
+ assertEquals("TestStatement", statement.getSid());
+ assertEquals("Allow", statement.getEffect());
+ assertEquals(Arrays.asList("storage:GetObject", "storage:PutObject"), statement.getActions());
+ assertEquals(Arrays.asList("storage://my-bucket/*"), statement.getResources());
+ assertEquals(Arrays.asList("arn:aws:iam::123456789012:user/TestUser"), statement.getPrincipals());
+
+ assertTrue(statement.getConditions().containsKey("StringEquals"));
+ assertEquals("us-west-2", statement.getConditions().get("StringEquals").get("aws:RequestedRegion"));
+ }
+
+ @Test
+ public void testStatementBuilderMinimal() {
+ Statement statement = Statement.builder()
+ .sid("MinimalStatement")
+ .effect("Deny")
+ .action("storage:DeleteObject")
+ .resource("storage://sensitive-bucket/*")
+ .build();
+
+ assertEquals("MinimalStatement", statement.getSid());
+ assertEquals("Deny", statement.getEffect());
+ assertEquals(Arrays.asList("storage:DeleteObject"), statement.getActions());
+ assertEquals(Arrays.asList("storage://sensitive-bucket/*"), statement.getResources());
+ assertTrue(statement.getPrincipals().isEmpty());
+ assertTrue(statement.getConditions().isEmpty());
+ }
+
+ @Test
+ public void testStatementBuilderMultipleResources() {
+ List expectedResources = Arrays.asList("storage://bucket1/*", "storage://bucket2/*");
+
+ Statement statement = Statement.builder()
+ .sid("MultiResourceStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://bucket1/*")
+ .resource("storage://bucket2/*")
+ .build();
+
+ assertEquals(expectedResources, statement.getResources());
+ }
+
+ @Test
+ public void testStatementBuilderMultiplePrincipals() {
+ List expectedPrincipals = Arrays.asList(
+ "arn:aws:iam::123456789012:user/User1",
+ "arn:aws:iam::123456789012:user/User2"
+ );
+
+ Statement statement = Statement.builder()
+ .sid("MultiPrincipalStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://shared-bucket/*")
+ .principal("arn:aws:iam::123456789012:user/User1")
+ .principal("arn:aws:iam::123456789012:user/User2")
+ .build();
+
+ assertEquals(expectedPrincipals, statement.getPrincipals());
+ }
+
+ @Test
+ public void testStatementBuilderMultipleConditions() {
+ Statement statement = Statement.builder()
+ .sid("MultiConditionStatement")
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://conditional-bucket/*")
+ .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .condition("DateGreaterThan", "aws:CurrentTime", "2024-01-01T00:00:00Z")
+ .build();
+
+ assertTrue(statement.getConditions().containsKey("StringEquals"));
+ assertTrue(statement.getConditions().containsKey("DateGreaterThan"));
+ assertEquals("us-west-2", statement.getConditions().get("StringEquals").get("aws:RequestedRegion"));
+ assertEquals("2024-01-01T00:00:00Z", statement.getConditions().get("DateGreaterThan").get("aws:CurrentTime"));
+ }
+
+ @Test
+ public void testStatementWithoutSid() {
+ Statement statement = Statement.builder()
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://no-sid-bucket/*")
+ .build();
+
+ assertNull(statement.getSid());
+ assertEquals("Allow", statement.getEffect());
+ }
+
+ @Test
+ public void testEmptyStatementThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ Statement.builder().build();
+ });
+ }
+
+ @Test
+ public void testStatementWithoutEffectThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ Statement.builder()
+ .sid("NoEffectStatement")
+ .action("storage:GetObject")
+ .resource("storage://test-bucket/*")
+ .build();
+ });
+ }
+
+ @Test
+ public void testStatementWithoutActionsThrowsException() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ Statement.builder()
+ .sid("NoActionsStatement")
+ .effect("Allow")
+ .resource("storage://test-bucket/*")
+ .build();
+ });
+ }
+
+ @Test
+ public void testStatementWithEmptyEffect() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ Statement.builder()
+ .sid("EmptyEffectStatement")
+ .effect("")
+ .action("storage:GetObject")
+ .build();
+ });
+ }
+
+ @Test
+ public void testStatementWithWhitespaceEffect() {
+ assertThrows(InvalidArgumentException.class, () -> {
+ Statement.builder()
+ .sid("WhitespaceEffectStatement")
+ .effect(" ")
+ .action("storage:GetObject")
+ .build();
+ });
+ }
+
+ @Test
+ public void testNullAndEmptyValueHandling() {
+ Statement statement = Statement.builder()
+ .effect("Allow")
+ .action(null)
+ .action("")
+ .action(" ")
+ .action("storage:GetObject")
+ .resource(null)
+ .resource("")
+ .resource(" ")
+ .resource("storage://test-bucket/*")
+ .principal(null)
+ .principal("")
+ .principal(" ")
+ .principal("valid-principal")
+ .condition(null, "key", "value")
+ .condition("StringEquals", null, "value")
+ .condition("StringEquals", "key", null)
+ .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .build();
+
+ assertEquals(1, statement.getActions().size());
+ assertEquals("storage:GetObject", statement.getActions().get(0));
+
+ assertEquals(1, statement.getResources().size());
+ assertEquals("storage://test-bucket/*", statement.getResources().get(0));
+
+ assertEquals(1, statement.getPrincipals().size());
+ assertEquals("valid-principal", statement.getPrincipals().get(0));
+
+ assertEquals(1, statement.getConditions().size());
+ assertTrue(statement.getConditions().containsKey("StringEquals"));
+ assertEquals("us-west-2", statement.getConditions().get("StringEquals").get("aws:RequestedRegion"));
+ }
+
+ @Test
+ public void testListMethodsWithNullValues() {
+ List principals = Arrays.asList("principal1", null, "", " ", "principal2");
+ List actions = Arrays.asList("storage:GetObject", null, "", " ", "storage:PutObject");
+ List resources = Arrays.asList("storage://bucket1/*", null, "", " ", "storage://bucket2/*");
+
+ Statement statement = Statement.builder()
+ .effect("Allow")
+ .actions(actions)
+ .resources(resources)
+ .principals(principals)
+ .build();
+
+ assertEquals(2, statement.getActions().size());
+ assertTrue(statement.getActions().contains("storage:GetObject"));
+ assertTrue(statement.getActions().contains("storage:PutObject"));
+
+ assertEquals(2, statement.getResources().size());
+ assertTrue(statement.getResources().contains("storage://bucket1/*"));
+ assertTrue(statement.getResources().contains("storage://bucket2/*"));
+
+ assertEquals(2, statement.getPrincipals().size());
+ assertTrue(statement.getPrincipals().contains("principal1"));
+ assertTrue(statement.getPrincipals().contains("principal2"));
+ }
+
+ @Test
+ public void testListMethodsWithNullLists() {
+ // Test that individual actions and resources are preserved even without using list methods
+ Statement statement = Statement.builder()
+ .effect("Allow")
+ .action("storage:GetObject")
+ .resource("storage://test-bucket/*")
+ .build();
+
+ assertEquals(1, statement.getActions().size());
+ assertEquals(1, statement.getResources().size());
+ assertTrue(statement.getPrincipals().isEmpty());
+ }
+
+}
\ No newline at end of file
diff --git a/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/TrustConfigurationTest.java b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/TrustConfigurationTest.java
new file mode 100644
index 00000000..0883dbd0
--- /dev/null
+++ b/iam/iam-client/src/test/java/com/salesforce/multicloudj/iam/model/TrustConfigurationTest.java
@@ -0,0 +1,130 @@
+package com.salesforce.multicloudj.iam.model;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for TrustConfiguration builder pattern and functionality.
+ */
+public class TrustConfigurationTest {
+
+ @Test
+ public void testTrustConfigurationBuilder() {
+ TrustConfiguration trustConfig = TrustConfiguration.builder()
+ .addTrustedPrincipal("arn:aws:iam::123456789012:root")
+ .addTrustedPrincipal("service-account@project.iam.gserviceaccount.com")
+ .addCondition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .addCondition("StringEquals", "aws:userid", "AIDACKCEVSQ6C2EXAMPLE")
+ .build();
+
+ List expectedPrincipals = Arrays.asList(
+ "arn:aws:iam::123456789012:root",
+ "service-account@project.iam.gserviceaccount.com"
+ );
+
+ assertEquals(expectedPrincipals, trustConfig.getTrustedPrincipals());
+
+ Map> conditions = trustConfig.getConditions();
+ assertTrue(conditions.containsKey("StringEquals"));
+ assertEquals("us-west-2", conditions.get("StringEquals").get("aws:RequestedRegion"));
+ assertEquals("AIDACKCEVSQ6C2EXAMPLE", conditions.get("StringEquals").get("aws:userid"));
+ }
+
+ @Test
+ public void testTrustConfigurationBuilderMinimal() {
+ TrustConfiguration trustConfig = TrustConfiguration.builder()
+ .addTrustedPrincipal("arn:aws:iam::987654321098:root")
+ .build();
+
+ assertEquals(Arrays.asList("arn:aws:iam::987654321098:root"), trustConfig.getTrustedPrincipals());
+ assertTrue(trustConfig.getConditions().isEmpty());
+ }
+
+ @Test
+ public void testTrustConfigurationBuilderSameOperatorMultipleConditions() {
+ TrustConfiguration trustConfig = TrustConfiguration.builder()
+ .addTrustedPrincipal("arn:aws:iam::123456789012:root")
+ .addCondition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .addCondition("StringEquals", "aws:userid", "AIDACKCEVSQ6C2EXAMPLE")
+ .build();
+
+ Map> conditions = trustConfig.getConditions();
+ assertTrue(conditions.containsKey("StringEquals"));
+
+ Map stringEqualsConditions = conditions.get("StringEquals");
+ assertEquals(2, stringEqualsConditions.size());
+ assertEquals("us-west-2", stringEqualsConditions.get("aws:RequestedRegion"));
+ assertEquals("AIDACKCEVSQ6C2EXAMPLE", stringEqualsConditions.get("aws:userid"));
+ }
+
+ @Test
+ public void testTrustConfigurationBuilderComplexScenario() {
+ TrustConfiguration trustConfig = TrustConfiguration.builder()
+ .addTrustedPrincipal("arn:aws:iam::123456789012:root") // AWS account
+ .addTrustedPrincipal("service-account@project.iam.gserviceaccount.com") // GCP service account
+ .addTrustedPrincipal("1234567890123456") // AliCloud account
+ .addCondition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .addCondition("StringEquals", "sts:ExternalId", "cross-cloud-external-id")
+ .addCondition("Bool", "aws:MultiFactorAuthPresent", "true")
+ .build();
+
+ List expectedPrincipals = Arrays.asList(
+ "arn:aws:iam::123456789012:root",
+ "service-account@project.iam.gserviceaccount.com",
+ "1234567890123456"
+ );
+
+ assertEquals(expectedPrincipals, trustConfig.getTrustedPrincipals());
+
+ Map> conditions = trustConfig.getConditions();
+ assertTrue(conditions.containsKey("StringEquals"));
+ assertTrue(conditions.containsKey("Bool"));
+
+ assertEquals("us-west-2", conditions.get("StringEquals").get("aws:RequestedRegion"));
+ assertEquals("cross-cloud-external-id", conditions.get("StringEquals").get("sts:ExternalId"));
+ assertEquals("true", conditions.get("Bool").get("aws:MultiFactorAuthPresent"));
+ }
+
+ @Test
+ public void testTrustConfigurationBuilderEmptyBuilder() {
+ TrustConfiguration trustConfig = TrustConfiguration.builder().build();
+
+ assertTrue(trustConfig.getTrustedPrincipals().isEmpty());
+ assertTrue(trustConfig.getConditions().isEmpty());
+ }
+
+ @Test
+ public void testNullAndEmptyValueHandling() {
+ // Test null and empty principals
+ TrustConfiguration trustConfig = TrustConfiguration.builder()
+ .addTrustedPrincipal("arn:aws:iam::123456789012:root")
+ .addTrustedPrincipal(null)
+ .addTrustedPrincipal("")
+ .addTrustedPrincipal(" ") // whitespace only
+ .addTrustedPrincipal("arn:aws:iam::987654321098:root")
+ .addTrustedPrincipals(null) // null list
+ .addTrustedPrincipals(Arrays.asList("valid-principal", null, "", " "))
+ .addCondition("StringEquals", "aws:RequestedRegion", "us-west-2")
+ .addCondition(null, "key", "value") // null operator
+ .addCondition("StringEquals", null, "value") // null key
+ .addCondition("StringEquals", "key", null) // null value
+ .build();
+
+ // Should only have valid principals
+ assertEquals(3, trustConfig.getTrustedPrincipals().size());
+ assertTrue(trustConfig.getTrustedPrincipals().contains("arn:aws:iam::123456789012:root"));
+ assertTrue(trustConfig.getTrustedPrincipals().contains("arn:aws:iam::987654321098:root"));
+ assertTrue(trustConfig.getTrustedPrincipals().contains("valid-principal"));
+
+ // Should only have valid condition
+ assertEquals(1, trustConfig.getConditions().size());
+ assertTrue(trustConfig.getConditions().containsKey("StringEquals"));
+ assertEquals("us-west-2", trustConfig.getConditions().get("StringEquals").get("aws:RequestedRegion"));
+ }
+}
\ No newline at end of file
diff --git a/iam/pom.xml b/iam/pom.xml
new file mode 100644
index 00000000..8ed08a44
--- /dev/null
+++ b/iam/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+
+ com.salesforce.multicloudj
+ multicloudj-parent
+ 0.2.11-SNAPSHOT
+ ../pom.xml
+
+ iam
+ pom
+ MultiCloudJ IAM
+ MultiCloudJ Identity and Access Management
+
+ iam-client
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 07db1871..e9ef8604 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,7 @@
+ iam
blob
docstore
examples
@@ -141,6 +142,17 @@
${project.version}
test-jar
+
+ com.salesforce.multicloudj
+ iam-client
+ ${project.version}
+
+
+ com.salesforce.multicloudj
+ iam-client
+ ${project.version}
+ test-jar
+
com.salesforce.multicloudj
blob-client