Skip to content

Commit a57e09d

Browse files
authored
iam: onboarding client layer for IAM (salesforce#90)
1 parent 228ab6f commit a57e09d

File tree

12 files changed

+1203
-0
lines changed

12 files changed

+1203
-0
lines changed

iam/iam-client/pom.xml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<artifactId>iam-client</artifactId>
6+
<packaging>jar</packaging>
7+
<name>MultiCloudJ - IAM Client</name>
8+
9+
<parent>
10+
<groupId>com.salesforce.multicloudj</groupId>
11+
<artifactId>iam</artifactId>
12+
<version>0.2.11-SNAPSHOT</version>
13+
<relativePath>../pom.xml</relativePath>
14+
</parent>
15+
16+
<dependencies>
17+
<!-- Core dependencies -->
18+
<dependency>
19+
<groupId>com.salesforce.multicloudj</groupId>
20+
<artifactId>multicloudj-common</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>com.salesforce.multicloudj</groupId>
24+
<artifactId>sts-client</artifactId>
25+
</dependency>
26+
27+
<!-- Lombok -->
28+
<dependency>
29+
<groupId>org.projectlombok</groupId>
30+
<artifactId>lombok</artifactId>
31+
<version>1.18.34</version>
32+
<scope>provided</scope>
33+
</dependency>
34+
35+
<!-- Test dependencies -->
36+
<dependency>
37+
<groupId>org.mockito</groupId>
38+
<artifactId>mockito-core</artifactId>
39+
<version>5.16.1</version>
40+
<scope>test</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.junit.jupiter</groupId>
44+
<artifactId>junit-jupiter-api</artifactId>
45+
<version>5.12.1</version>
46+
<scope>test</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.mockito</groupId>
50+
<artifactId>mockito-junit-jupiter</artifactId>
51+
<version>5.16.1</version>
52+
<scope>test</scope>
53+
</dependency>
54+
<dependency>
55+
<groupId>com.salesforce.multicloudj</groupId>
56+
<artifactId>multicloudj-common</artifactId>
57+
<type>test-jar</type>
58+
<scope>test</scope>
59+
</dependency>
60+
</dependencies>
61+
62+
<build>
63+
<plugins>
64+
<plugin>
65+
<groupId>org.apache.maven.plugins</groupId>
66+
<artifactId>maven-jar-plugin</artifactId>
67+
<version>3.4.2</version>
68+
<executions>
69+
<execution>
70+
<goals>
71+
<goal>test-jar</goal>
72+
</goals>
73+
</execution>
74+
</executions>
75+
</plugin>
76+
77+
</plugins>
78+
</build>
79+
</project>
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package com.salesforce.multicloudj.iam.client;
2+
3+
import com.salesforce.multicloudj.iam.model.CreateOptions;
4+
import com.salesforce.multicloudj.iam.model.PolicyDocument;
5+
import com.salesforce.multicloudj.iam.model.TrustConfiguration;
6+
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
7+
8+
import java.net.URI;
9+
import java.util.List;
10+
import java.util.Optional;
11+
12+
/**
13+
* Entry point for client code to interact with Identity and Access Management (IAM) services
14+
* in a substrate-agnostic way.
15+
*
16+
* <p>This client provides unified IAM operations across multiple cloud providers including
17+
* AWS IAM, GCP IAM, and AliCloud RAM. It handles the complexity of different cloud IAM models
18+
* and provides a consistent API for identity lifecycle management and policy operations.
19+
*
20+
* <p>Usage example:
21+
* <pre>
22+
* IamClient client = IamClient.builder("aws")
23+
* .withRegion("us-west-2")
24+
* .build();
25+
*
26+
* // Create identity
27+
* String identityId = client.createIdentity("MyRole", "Example role", "123456789012", "us-west-2",
28+
* Optional.empty(), Optional.empty());
29+
*
30+
* // Create policy
31+
* PolicyDocument policy = PolicyDocument.builder()
32+
* .version("2012-10-17") // Use provider-specific version (AWS example)
33+
* .statement("StorageAccess")
34+
* .effect("Allow")
35+
* .addAction("storage:GetObject")
36+
* .addResource("storage://my-bucket/*")
37+
* .endStatement()
38+
* .build();
39+
*
40+
* // Attach policy
41+
* client.attachInlinePolicy(policy, "123456789012", "us-west-2", "my-bucket");
42+
* </pre>
43+
*/
44+
public class IamClient {
45+
46+
/**
47+
* Protected constructor for IamClient.
48+
* Use the builder pattern to create instances.
49+
*/
50+
protected IamClient() {
51+
// Implementation will be added later when AbstractIamService is available
52+
}
53+
54+
/**
55+
* Creates a new IamClientBuilder for the specified provider.
56+
*
57+
* @param providerId the ID of the provider such as "aws", "gcp", or "ali"
58+
* @return a new IamClientBuilder instance
59+
*/
60+
public static IamClientBuilder builder(String providerId) {
61+
return new IamClientBuilder(providerId);
62+
}
63+
64+
/**
65+
* Creates a new identity (role/service account) in the cloud provider.
66+
*
67+
* @param identityName the name of the identity to create
68+
* @param description optional description for the identity (can be null)
69+
* @param tenantId the tenant ID (AWS Account ID, GCP Project ID, or AliCloud Account ID)
70+
* @param region the region for IAM operations
71+
* @param trustConfig optional trust configuration
72+
* @param options optional creation options
73+
* @return the unique identifier of the created identity
74+
*/
75+
public String createIdentity(String identityName, String description, String tenantId, String region,
76+
Optional<TrustConfiguration> trustConfig, Optional<CreateOptions> options) {
77+
// Implementation will be added when driver layer is available
78+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
79+
}
80+
81+
/**
82+
* Attaches an inline policy to a resource.
83+
*
84+
* @param policyDocument the policy document in substrate-neutral format
85+
* @param tenantId the tenant ID
86+
* @param region the region
87+
* @param resource the resource to attach the policy to
88+
*/
89+
public void attachInlinePolicy(PolicyDocument policyDocument, String tenantId, String region, String resource) {
90+
// Implementation will be added when driver layer is available
91+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
92+
}
93+
94+
/**
95+
* Retrieves the details of a specific inline policy attached to an identity.
96+
*
97+
* @param identityName the name of the identity
98+
* @param policyName the name of the policy
99+
* @param tenantId the tenant ID
100+
* @param region the region
101+
* @return the policy document details as a string
102+
*/
103+
public String getInlinePolicyDetails(String identityName, String policyName, String tenantId, String region) {
104+
// Implementation will be added when driver layer is available
105+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
106+
}
107+
108+
/**
109+
* Lists all inline policies attached to an identity.
110+
*
111+
* @param identityName the name of the identity
112+
* @param tenantId the tenant ID
113+
* @param region the region
114+
* @return a list of policy names
115+
*/
116+
public List<String> getAttachedPolicies(String identityName, String tenantId, String region) {
117+
// Implementation will be added when driver layer is available
118+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
119+
}
120+
121+
/**
122+
* Removes an inline policy from an identity.
123+
*
124+
* @param identityName the name of the identity
125+
* @param policyName the name of the policy to remove
126+
* @param tenantId the tenant ID
127+
* @param region the region
128+
*/
129+
public void removePolicy(String identityName, String policyName, String tenantId, String region) {
130+
// Implementation will be added when driver layer is available
131+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
132+
}
133+
134+
/**
135+
* Deletes an identity from the cloud provider.
136+
*
137+
* @param identityName the name of the identity to delete
138+
* @param tenantId the tenant ID
139+
* @param region the region
140+
*/
141+
public void deleteIdentity(String identityName, String tenantId, String region) {
142+
// Implementation will be added when driver layer is available
143+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
144+
}
145+
146+
/**
147+
* Retrieves metadata about an identity.
148+
*
149+
* @param identityName the name of the identity
150+
* @param tenantId the tenant ID
151+
* @param region the region
152+
* @return the unique identity identifier (ARN, email, or roleId)
153+
*/
154+
public String getIdentity(String identityName, String tenantId, String region) {
155+
// Implementation will be added when driver layer is available
156+
throw new UnsupportedOperationException("Implementation will be added when driver layer is available");
157+
}
158+
159+
/**
160+
* Builder class for IamClient.
161+
*/
162+
public static class IamClientBuilder {
163+
protected String region;
164+
protected URI endpoint;
165+
166+
/**
167+
* Constructor for IamClientBuilder.
168+
*
169+
* @param providerId the ID of the provider such as "aws", "gcp", or "ali"
170+
*/
171+
public IamClientBuilder(String providerId) {
172+
// Implementation will be added when ServiceLoader and AbstractIamService are available
173+
// Will find and initialize the provider builder here
174+
}
175+
176+
/**
177+
* Sets the region for the IAM client.
178+
*
179+
* @param region the region to set
180+
* @return this IamClientBuilder instance
181+
*/
182+
public IamClientBuilder withRegion(String region) {
183+
this.region = region;
184+
// Implementation will be added later to delegate to underlying provider builder
185+
return this;
186+
}
187+
188+
/**
189+
* Sets the endpoint to override for the IAM client.
190+
*
191+
* @param endpoint the endpoint to set
192+
* @return this IamClientBuilder instance
193+
*/
194+
public IamClientBuilder withEndpoint(URI endpoint) {
195+
this.endpoint = endpoint;
196+
// Implementation will be added later to delegate to underlying provider builder
197+
return this;
198+
}
199+
200+
/**
201+
* Builds and returns an IamClient instance.
202+
*
203+
* @return a new IamClient instance
204+
*/
205+
public IamClient build() {
206+
// Implementation will be added when ServiceLoader and AbstractIamService are available
207+
return new IamClient();
208+
}
209+
}
210+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.salesforce.multicloudj.iam.model;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
/**
7+
* Optional configuration for identity creation operations.
8+
*
9+
* <p>This class provides additional options that can be set during identity creation,
10+
* such as path specifications, session duration limits, and permission boundaries.
11+
*
12+
* <p>Permission boundary identifiers are provider-specific and translated internally
13+
* by the implementation layer. The client accepts the native format for the target
14+
* cloud provider.
15+
*
16+
* <p>Usage example:
17+
* <pre>
18+
* CreateOptions options = CreateOptions.builder()
19+
* .path("/service-roles/")
20+
* .maxSessionDuration(3600) // 1 hour in seconds
21+
* .permissionBoundary("policy-identifier") // Provider-specific format
22+
* .build();
23+
* </pre>
24+
*/
25+
@Getter
26+
@Builder
27+
public class CreateOptions {
28+
private final String path;
29+
private final Integer maxSessionDuration;
30+
private final String permissionBoundary;
31+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.salesforce.multicloudj.iam.model;
2+
3+
import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException;
4+
import java.util.List;
5+
import java.util.Objects;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.Singular;
9+
10+
/**
11+
* Represents a substrate-neutral policy document containing multiple statements.
12+
*
13+
* <p>This class provides a cloud-agnostic way to define IAM policies that can be
14+
* translated to AWS, GCP, or AliCloud native formats. The policy uses a builder
15+
* pattern to prevent JSON parsing errors and provides type safety.
16+
*
17+
* <p>Usage example:
18+
* <pre>
19+
* PolicyDocument policy = PolicyDocument.builder()
20+
* .version("2012-10-17")
21+
* .statement(Statement.builder()
22+
* .sid("StorageAccess")
23+
* .effect("Allow")
24+
* .action("storage:GetObject")
25+
* .action("storage:PutObject")
26+
* .principal("arn:aws:iam::123456789012:user/ExampleUser")
27+
* .resource("storage://my-bucket/*")
28+
* .condition("StringEquals", "aws:RequestedRegion", "us-west-2")
29+
* .build())
30+
* .build();
31+
* </pre>
32+
*/
33+
@Getter
34+
public class PolicyDocument {
35+
private final String version;
36+
private final List<Statement> statements;
37+
38+
@Builder
39+
private PolicyDocument(String version, @Singular List<Statement> statements) {
40+
// Validate version is provided
41+
if (version == null) {
42+
throw new InvalidArgumentException("Version is required");
43+
}
44+
45+
// Filter out null statements and validate at least one exists
46+
List<Statement> filteredStatements = statements != null
47+
? statements.stream().filter(Objects::nonNull)
48+
.collect(java.util.stream.Collectors.toList())
49+
: new java.util.ArrayList<>();
50+
51+
if (filteredStatements.isEmpty()) {
52+
throw new InvalidArgumentException("At least one statement is required");
53+
}
54+
55+
this.version = version;
56+
this.statements = filteredStatements;
57+
}
58+
}

0 commit comments

Comments
 (0)