package core.aws.client;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.policy.Policy;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClientBuilder;
import com.amazonaws.services.identitymanagement.model.AddRoleToInstanceProfileRequest;
import com.amazonaws.services.identitymanagement.model.CreateInstanceProfileRequest;
import com.amazonaws.services.identitymanagement.model.CreateRoleRequest;
import com.amazonaws.services.identitymanagement.model.DeleteServerCertificateRequest;
import com.amazonaws.services.identitymanagement.model.GetRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.GetRolePolicyResult;
import com.amazonaws.services.identitymanagement.model.GetServerCertificateRequest;
import com.amazonaws.services.identitymanagement.model.InstanceProfile;
import com.amazonaws.services.identitymanagement.model.ListInstanceProfilesRequest;
import com.amazonaws.services.identitymanagement.model.ListInstanceProfilesResult;
import com.amazonaws.services.identitymanagement.model.ListServerCertificatesRequest;
import com.amazonaws.services.identitymanagement.model.ListServerCertificatesResult;
import com.amazonaws.services.identitymanagement.model.NoSuchEntityException;
import com.amazonaws.services.identitymanagement.model.PutRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.ServerCertificate;
import com.amazonaws.services.identitymanagement.model.ServerCertificateMetadata;
import com.amazonaws.services.identitymanagement.model.UploadServerCertificateRequest;
import com.amazonaws.services.identitymanagement.model.UploadServerCertificateResult;
import core.aws.util.Asserts;
import core.aws.util.Encodings;
import core.aws.util.Runner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
/**
* @author neo
*/
public class IAM {
public final AmazonIdentityManagement iam;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Region region;
public IAM(AWSCredentialsProvider credentials, Regions region) {
iam = AmazonIdentityManagementClientBuilder.standard().withRegion(region).withCredentials(credentials).build();
this.region = Region.getRegion(region);
}
public ServerCertificate createServerCert(UploadServerCertificateRequest request) throws InterruptedException {
logger.info("create server cert, path={}, name={}", request.getPath(), request.getServerCertificateName());
UploadServerCertificateResult result = iam.uploadServerCertificate(request);
return new ServerCertificate(result.getServerCertificateMetadata(), request.getCertificateBody())
.withCertificateChain(request.getCertificateChain());
}
public void deleteServerCert(String name) throws Exception {
logger.info("delete server cert, name={}", name);
// after delete ELB listener, it may not be visible to IAM immediately
new Runner<Void>()
.maxAttempts(3)
.retryInterval(Duration.ofSeconds(20))
.retryOn(e -> e instanceof AmazonServiceException && "DeleteConflict".equals(((AmazonServiceException) e).getErrorCode()))
.run(() -> {
iam.deleteServerCertificate(new DeleteServerCertificateRequest(name));
return null;
});
}
public List<ServerCertificateMetadata> listServerCerts(String path) {
logger.info("list server certs, path={}", path);
ListServerCertificatesResult result = iam.listServerCertificates(new ListServerCertificatesRequest().withPathPrefix(path));
return result.getServerCertificateMetadataList();
}
public List<InstanceProfile> listInstanceProfiles(String path) {
logger.info("list instance profiles, path={}", path);
ListInstanceProfilesResult result = iam.listInstanceProfiles(new ListInstanceProfilesRequest().withPathPrefix(path).withMaxItems(1000));
Asserts.isFalse(result.isTruncated(), "result is truncated, update to support more instance profiles");
return result.getInstanceProfiles();
}
public InstanceProfile createInstanceProfile(String path, String name, String policy) {
CreateInstanceProfileRequest request = new CreateInstanceProfileRequest()
.withPath(path)
.withInstanceProfileName(name);
logger.info("create instance profile, path={}, name={}", path, name);
InstanceProfile instanceProfile = iam.createInstanceProfile(request).getInstanceProfile();
logger.info("create role, name={}", name);
iam.createRole(new CreateRoleRequest()
.withRoleName(name)
.withPath(path)
.withAssumeRolePolicyDocument(assumeRolePolicyDocument()));
// attach role to instance before creating policy, if policy failed, at least profile/role are ready, and policy can be fixed thru AWS console
iam.addRoleToInstanceProfile(new AddRoleToInstanceProfileRequest()
.withInstanceProfileName(name)
.withRoleName(name));
createRolePolicy(name, name, policy);
return instanceProfile;
}
String assumeRolePolicyDocument() {
String service = "ec2." + region.getDomain();
return "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"" + service + "\"},\"Action\":\"sts:AssumeRole\"}]}";
}
public ServerCertificate getServerCert(String serverCertName) {
logger.info("get server cert, name={}", serverCertName);
return iam.getServerCertificate(new GetServerCertificateRequest(serverCertName)).getServerCertificate();
}
public Optional<Policy> findRolePolicy(String roleName, String policyName) {
logger.info("find role policy, roleName={}, policyName={}", roleName, policyName);
try {
GetRolePolicyResult result = iam.getRolePolicy(new GetRolePolicyRequest()
.withRoleName(roleName)
.withPolicyName(policyName));
String policyJSON = Encodings.decodeURL(result.getPolicyDocument());
return Optional.of(Policy.fromJson(policyJSON));
} catch (NoSuchEntityException e) {
return Optional.empty();
}
}
public void createRolePolicy(String roleName, String policyName, String policyJSON) {
logger.info("create role policy, role={}, policyName={}, policyJSON={}", roleName, policyName, policyJSON);
iam.putRolePolicy(new PutRolePolicyRequest()
.withRoleName(roleName)
.withPolicyName(policyName)
.withPolicyDocument(policyJSON));
}
}