Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor credentials providers #963

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
public class AuthConstant {
public static final String SYSTEM_ACCESSKEYID = "alibabacloud.accessKeyId";
public static final String SYSTEM_ACCESSKEYSECRET = "alibabacloud.accessKeyIdSecret";
public static final String SYSTEM_ACCESSKEY_SECRET = "alibabacloud.accessKeySecret";
public static final String SYSTEM_SESSION_TOKEN = "alibabacloud.sessionToken";

public static final String DEFAULT_CREDENTIALS_FILE_PATH = System.getProperty("user.home") +
"/.alibabacloud/credentials.ini";
Expand All @@ -11,6 +13,7 @@ public class AuthConstant {
public static final String INI_TYPE = "type";
public static final String INI_TYPE_RAM = "ecs_ram_role";
public static final String INI_TYPE_ARN = "ram_role_arn";
public static final String INI_TYPE_OIDC = "oidc_role_arn";
public static final String INI_TYPE_KEY_PAIR = "rsa_key_pair";
public static final String INI_PUBLIC_KEY_ID = "public_key_id";
public static final String INI_PRIVATE_KEY_FILE = "private_key_file";
Expand All @@ -19,6 +22,8 @@ public class AuthConstant {
public static final String INI_ROLE_SESSION_NAME = "role_session_name";
public static final String INI_ROLE_ARN = "role_arn";
public static final String INI_POLICY = "policy";
public static final String INI_OIDC_PROVIDER_ARN = "oidc_provider_arn";
public static final String INI_OIDC_TOKEN_FILE_PATH = "oidc_token_file_path";
public static final long TSC_VALID_TIME_SECONDS = 3600L;
public static final String DEFAULT_REGION = "region_id";
public static final String INI_ENABLE = "enable";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
package com.aliyuncs.auth;

import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.utils.AuthUtils;
import com.aliyuncs.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;

public class CLIProfileCredentialsProvider implements AlibabaCloudCredentialsProvider {
private final String CLI_CREDENTIALS_CONFIG_PATH = System.getProperty("user.home") +
"/.aliyun/config.json";
private volatile AlibabaCloudCredentialsProvider credentialsProvider;
private volatile String currentProfileName;
private final Object credentialsProviderLock = new Object();

private CLIProfileCredentialsProvider(Builder builder) {
this.currentProfileName = builder.profileName == null ? System.getenv("ALIBABA_CLOUD_PROFILE") : builder.profileName;
}

public static Builder builder() {
return new Builder();
}

@Override
public AlibabaCloudCredentials getCredentials() throws ClientException {
if (AuthUtils.isDisableCLIProfile()) {
throw new ClientException("CLI credentials file is disabled.");
}
Config config = null;
try {
config = parseProfile(CLI_CREDENTIALS_CONFIG_PATH);
} catch (IOException e) {
throw new ClientException("Unable to get profile from CLI credentials file: " + e);
}
if (null == config) {
throw new ClientException("Unable to get profile from empty CLI credentials file.");
}
String refreshedProfileName = System.getenv("ALIBABA_CLOUD_PROFILE");
if (shouldReloadCredentialsProvider(refreshedProfileName)) {
synchronized (credentialsProviderLock) {
if (shouldReloadCredentialsProvider(refreshedProfileName)) {
if (!StringUtils.isEmpty(refreshedProfileName)) {
this.currentProfileName = refreshedProfileName;
}
this.credentialsProvider = reloadCredentialsProvider(config, this.currentProfileName);
}
}
}
return this.credentialsProvider.getCredentials();
}

AlibabaCloudCredentialsProvider reloadCredentialsProvider(Config config, String profileName) throws ClientException {
String currentProfileName = !StringUtils.isEmpty(profileName) ? profileName : config.getCurrent();
List<Profile> profiles = config.getProfiles();
if (profiles != null && !profiles.isEmpty()) {
for (Profile profile : profiles) {
if (!StringUtils.isEmpty(profile.getName()) && profile.getName().equals(currentProfileName)) {
String mode = profile.getMode();

if ("AK".equals(mode)) {
return new StaticCredentialsProvider(
new BasicCredentials(profile.getAccessKeyId(), profile.getAccessKeySecret()));
} else if ("RamRoleArn".equals(mode)) {
AlibabaCloudCredentialsProvider innerProvider = new StaticCredentialsProvider(
new BasicCredentials(profile.getAccessKeyId(), profile.getAccessKeySecret()));
return STSAssumeRoleSessionCredentialsProvider.builder()
.credentialsProvider(innerProvider)
.durationSeconds(profile.getDurationSeconds())
.roleArn(profile.getRoleArn())
.roleSessionName(profile.getRoleSessionName())
.stsRegionId(profile.getStsRegionId())
.enableVpc(profile.getEnableVpc())
.policy(profile.getPolicy())
.externalId(profile.getExternalId())
.build();
} else if ("EcsRamRole".equals(mode)) {
return InstanceProfileCredentialsProvider.builder()
.roleName(profile.getRamRoleName())
.build();
} else if ("OIDC".equals(mode)) {
return OIDCCredentialsProvider.builder()
.durationSeconds(profile.getDurationSeconds())
.roleArn(profile.getRoleArn())
.roleSessionName(profile.getRoleSessionName())
.oidcProviderArn(profile.getOidcProviderArn())
.oidcTokenFilePath(profile.getOidcTokenFile())
.stsRegionId(profile.getStsRegionId())
.enableVpc(profile.getEnableVpc())
.policy(profile.getPolicy())
.build();
} else if ("ChainableRamRoleArn".equals(mode)) {
AlibabaCloudCredentialsProvider previousProvider = reloadCredentialsProvider(config, profile.getSourceProfile());
return STSAssumeRoleSessionCredentialsProvider.builder()
.credentialsProvider(previousProvider)
.durationSeconds(profile.getDurationSeconds())
.roleArn(profile.getRoleArn())
.roleSessionName(profile.getRoleSessionName())
.stsRegionId(profile.getStsRegionId())
.enableVpc(profile.getEnableVpc())
.policy(profile.getPolicy())
.externalId(profile.getExternalId())
.build();
} else {
throw new ClientException(String.format("Unsupported profile mode '%s' form CLI credentials file.", mode));
}
}
}
}
throw new ClientException(String.format("Unable to get profile with '%s' form CLI credentials file.", currentProfileName));
}

Config parseProfile(String configFilePath) throws ClientException, IOException {
File configFile = new File(configFilePath);
if (!configFile.exists() || !configFile.isFile() || !configFile.canRead()) {
throw new ClientException(String.format("Unable to open credentials file: %s.", configFile.getAbsolutePath()));
}
Gson gson = new Gson();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(configFile));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
String jsonContent = sb.toString();
return gson.fromJson(jsonContent, Config.class);
} catch (Exception e) {
throw new ClientException(String.format("Failed to parse credential form CLI credentials file: %s.", configFile.getAbsolutePath()));
} finally {
if (br != null) {
br.close();
}
}
}

boolean shouldReloadCredentialsProvider(String profileName) {
return this.credentialsProvider == null || (!StringUtils.isEmpty(this.currentProfileName) && !StringUtils.isEmpty(profileName) && !this.currentProfileName.equals(profileName));
}

String getProfileName() {
return this.currentProfileName;
}

static final class Builder {
private String profileName;

public Builder profileName(String profileName) {
this.profileName = profileName;
return this;
}

CLIProfileCredentialsProvider build() {
return new CLIProfileCredentialsProvider(this);
}
}

static class Config {
@SerializedName("current")
private String current;
@SerializedName("profiles")
private List<Profile> profiles;

public String getCurrent() {
return current;
}

public List<Profile> getProfiles() {
return profiles;
}
}

static class Profile {
@SerializedName("name")
private String name;
@SerializedName("mode")
private String mode;
@SerializedName("access_key_id")
private String accessKeyId;
@SerializedName("access_key_secret")
private String accessKeySecret;
@SerializedName("ram_role_arn")
private String roleArn;
@SerializedName("ram_session_name")
private String roleSessionName;
@SerializedName("expired_seconds")
private Integer durationSeconds;
@SerializedName("sts_region")
private String stsRegionId;
@SerializedName("enable_vpc")
private Boolean enableVpc;
@SerializedName("ram_role_name")
private String ramRoleName;
@SerializedName("oidc_token_file")
private String oidcTokenFile;
@SerializedName("oidc_provider_arn")
private String oidcProviderArn;
@SerializedName("source_profile")
private String sourceProfile;
@SerializedName("policy")
private String policy;
@SerializedName("external_id")
private String externalId;

public String getName() {
return name;
}

public String getMode() {
return mode;
}

public String getAccessKeyId() {
return accessKeyId;
}

public String getAccessKeySecret() {
return accessKeySecret;
}

public String getRoleArn() {
return roleArn;
}

public String getRoleSessionName() {
return roleSessionName;
}

public Integer getDurationSeconds() {
return durationSeconds;
}

public String getStsRegionId() {
return stsRegionId;
}

public Boolean getEnableVpc() {
return enableVpc;
}

public String getRamRoleName() {
return ramRoleName;
}

public String getOidcTokenFile() {
return oidcTokenFile;
}

public String getOidcProviderArn() {
return oidcProviderArn;
}

public String getSourceProfile() {
return sourceProfile;
}

public String getPolicy() {
return policy;
}

public String getExternalId() {
return externalId;
}
}
}
Loading
Loading