Skip to content

Commit 3b2eb24

Browse files
committed
add configuration for ApiClient
1 parent 43247d8 commit 3b2eb24

File tree

7 files changed

+318
-17
lines changed

7 files changed

+318
-17
lines changed

core/src/main/java/cloud/stackit/sdk/core/KeyFlowAuthenticator.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package cloud.stackit.sdk.core;
22

3-
import cloud.stackit.sdk.core.model.ServiceAccountCredentials;
43
import cloud.stackit.sdk.core.model.ServiceAccountKey;
54
import com.auth0.jwt.JWT;
65
import com.auth0.jwt.algorithms.Algorithm;
@@ -9,12 +8,10 @@
98
import okhttp3.*;
109

1110
import java.io.IOException;
12-
import java.io.InputStream;
1311
import java.io.InputStreamReader;
1412
import java.net.HttpURLConnection;
1513
import java.nio.charset.StandardCharsets;
1614
import java.security.interfaces.RSAPrivateKey;
17-
import java.security.interfaces.RSAPublicKey;
1815
import java.util.Date;
1916
import java.util.HashMap;
2017
import java.util.Map;
@@ -64,7 +61,6 @@ public KeyFlowAuthenticator(ServiceAccountKey saKey) {
6461
createAccessToken();
6562
}
6663

67-
6864
public synchronized String getAccessToken() throws IOException {
6965
if (token == null || token.isExpired()) {
7066
createAccessTokenWithRefreshToken();

core/src/main/java/cloud/stackit/sdk/core/keyflow/KeyFlowInterceptor.java renamed to core/src/main/java/cloud/stackit/sdk/core/KeyFlowInterceptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package cloud.stackit.sdk.core.keyflow;
1+
package cloud.stackit.sdk.core;
22

3-
import cloud.stackit.sdk.core.KeyFlowAuthenticator;
43
import okhttp3.Interceptor;
54
import okhttp3.Request;
65
import okhttp3.Response;
6+
import org.jetbrains.annotations.NotNull;
77

88
import java.io.IOException;
99

@@ -14,6 +14,7 @@ public KeyFlowInterceptor(KeyFlowAuthenticator authenticator) {
1414
this.authenticator = authenticator;
1515
}
1616

17+
@NotNull
1718
@Override
1819
public Response intercept(Chain chain) throws IOException {
1920
Request originalRequest = chain.request();
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package cloud.stackit.sdk.core.auth;
2+
3+
import cloud.stackit.sdk.core.KeyFlowAuthenticator;
4+
import cloud.stackit.sdk.core.config.Configuration;
5+
import cloud.stackit.sdk.core.config.EnvironmentVariables;
6+
import cloud.stackit.sdk.core.KeyFlowInterceptor;
7+
import cloud.stackit.sdk.core.model.ServiceAccountKey;
8+
import com.google.gson.Gson;
9+
import com.google.gson.reflect.TypeToken;
10+
import okhttp3.Interceptor;
11+
12+
import java.lang.reflect.Type;
13+
import java.nio.charset.StandardCharsets;
14+
import java.nio.file.Files;
15+
import java.nio.file.Paths;
16+
import java.util.Map;
17+
18+
public class SetupAuth {
19+
private Interceptor authHandler;
20+
private final String defaultCredentialsFilePath = "~/.stackit/credentials.json";
21+
22+
public SetupAuth() {
23+
this(new Configuration.Builder().build());
24+
}
25+
26+
public SetupAuth(Configuration cfg) {
27+
if (cfg == null) {
28+
cfg = new Configuration.Builder().build();
29+
}
30+
31+
try {
32+
ServiceAccountKey saKey = setupKeyFlow(cfg);
33+
authHandler = new KeyFlowInterceptor(new KeyFlowAuthenticator(saKey));
34+
} catch (Exception e) {
35+
e.printStackTrace();
36+
}
37+
}
38+
39+
public Interceptor getAuthHandler() {
40+
return authHandler;
41+
}
42+
43+
private ServiceAccountKey setupKeyFlow(Configuration cfg) throws Exception {
44+
ServiceAccountKey saKey = null;
45+
// Explicit config in code
46+
if (cfg.getServiceAccountKey() != null && !cfg.getServiceAccountKey().trim().isEmpty()) {
47+
saKey = ServiceAccountKey.loadCredentials(cfg.getServiceAccountKey());
48+
loadPrivateKey(cfg, saKey);
49+
return saKey;
50+
}
51+
52+
if (cfg.getServiceAccountKeyPath() != null && !cfg.getServiceAccountKeyPath().trim().isEmpty()) {
53+
String fileContent = new String(Files.readAllBytes(Paths.get(cfg.getServiceAccountKeyPath())), StandardCharsets.UTF_8);
54+
saKey = new Gson().fromJson(fileContent, ServiceAccountKey.class);
55+
loadPrivateKey(cfg, saKey);
56+
return saKey;
57+
}
58+
59+
// Env config
60+
if (!EnvironmentVariables.STACKIT_SERVICE_ACCOUNT_KEY.trim().isEmpty()) {
61+
saKey = ServiceAccountKey.loadCredentials(EnvironmentVariables.STACKIT_SERVICE_ACCOUNT_KEY.trim());
62+
loadPrivateKey(cfg, saKey);
63+
return saKey;
64+
}
65+
66+
if (!EnvironmentVariables.STACKIT_SERVICE_ACCOUNT_KEY_PATH.trim().isEmpty()) {
67+
String fileContent = new String(Files.readAllBytes(Paths.get(cfg.getServiceAccountKeyPath())), StandardCharsets.UTF_8);
68+
saKey = new Gson().fromJson(fileContent, ServiceAccountKey.class);
69+
loadPrivateKey(cfg, saKey);
70+
return saKey;
71+
}
72+
73+
if (!EnvironmentVariables.STACKIT_CREDENTIALS_PATH.trim().isEmpty()) {
74+
String saKeyJson = readValueFromCredentialsFile(EnvironmentVariables.STACKIT_CREDENTIALS_PATH, EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH);
75+
saKey = new Gson().fromJson(saKeyJson, ServiceAccountKey.class);
76+
loadPrivateKey(cfg, saKey);
77+
return saKey;
78+
} else {
79+
try {
80+
String saKeyJson = readValueFromCredentialsFile(defaultCredentialsFilePath, EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY, EnvironmentVariables.ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH);
81+
saKey = new Gson().fromJson(saKeyJson, ServiceAccountKey.class);
82+
loadPrivateKey(cfg, saKey);
83+
return saKey;
84+
} catch (Exception e) {
85+
throw new Exception("could not find service account key");
86+
}
87+
}
88+
}
89+
90+
private void loadPrivateKey(Configuration cfg, ServiceAccountKey saKey) throws Exception {
91+
if (!saKey.getCredentials().isPrivateKeySet()) {
92+
try {
93+
String privateKey = getPrivateKey(cfg);
94+
saKey.getCredentials().setPrivateKey(privateKey);
95+
} catch (Exception e) {
96+
throw new Exception("could not find private key", e);
97+
}
98+
}
99+
}
100+
101+
private String getPrivateKey(Configuration cfg) throws Exception {
102+
// Explicit code config
103+
// Set private key
104+
if (cfg.getPrivateKey() != null && !cfg.getPrivateKey().trim().isEmpty()) {
105+
return cfg.getPrivateKey();
106+
}
107+
// Set private key path
108+
if (cfg.getPrivateKeyPath() != null && !cfg.getPrivateKeyPath().trim().isEmpty()) {
109+
String privateKeyPath = cfg.getPrivateKeyPath();
110+
return new String(Files.readAllBytes(Paths.get(privateKeyPath)), StandardCharsets.UTF_8);
111+
}
112+
// Set credentials file
113+
if (cfg.getCredentialsFilePath() != null && !cfg.getCredentialsFilePath().trim().isEmpty()) {
114+
return readValueFromCredentialsFile(cfg.getCredentialsFilePath(), EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY, EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY_PATH);
115+
}
116+
117+
// ENVs config
118+
if (EnvironmentVariables.STACKIT_PRIVATE_KEY != null && !EnvironmentVariables.STACKIT_PRIVATE_KEY.trim().isEmpty()) {
119+
return EnvironmentVariables.STACKIT_PRIVATE_KEY.trim();
120+
}
121+
if (EnvironmentVariables.STACKIT_PRIVATE_KEY_PATH != null && !EnvironmentVariables.STACKIT_PRIVATE_KEY_PATH.trim().isEmpty()) {
122+
return new String(Files.readAllBytes(Paths.get(EnvironmentVariables.STACKIT_PRIVATE_KEY_PATH)), StandardCharsets.UTF_8);
123+
}
124+
if (EnvironmentVariables.STACKIT_CREDENTIALS_PATH != null && !EnvironmentVariables.STACKIT_CREDENTIALS_PATH.trim().isEmpty()) {
125+
return readValueFromCredentialsFile(EnvironmentVariables.STACKIT_CREDENTIALS_PATH, EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY, EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY_PATH);
126+
}
127+
128+
// Read from credentials file in defaultCredentialsFilePath
129+
return readValueFromCredentialsFile(defaultCredentialsFilePath, EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY, EnvironmentVariables.ENV_STACKIT_PRIVATE_KEY_PATH);
130+
}
131+
132+
private String readValueFromCredentialsFile(String path, String valueKey, String pathKey) throws Exception {
133+
// Read credentials file
134+
String fileContent = new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
135+
Type credentialsFileType = new TypeToken<Map<String, String>>(){}.getType();
136+
Map<String, String> map = new Gson().fromJson(fileContent, credentialsFileType);
137+
138+
// Read STACKIT_PRIVATE_KEY from credentials file
139+
String privateKey = map.get(valueKey);
140+
if (privateKey != null && !privateKey.trim().isEmpty()) {
141+
return privateKey;
142+
}
143+
144+
// Read STACKIT_PRIVATE_KEY_PATH from credentials file
145+
String privateKeyPath = map.get(pathKey);
146+
if (privateKeyPath != null && !privateKeyPath.trim().isEmpty()) {
147+
return new String(Files.readAllBytes(Paths.get(privateKeyPath)));
148+
}
149+
throw new Exception("could not find private key");
150+
}
151+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package cloud.stackit.sdk.core.config;
2+
3+
import java.util.Map;
4+
5+
public class Configuration {
6+
private final Map<String, String> defaultHeader;
7+
private final String serviceAccountKey;
8+
private final String serviceAccountKeyPath;
9+
private final String privateKeyPath;
10+
private final String privateKey;
11+
private final String customEndpoint;
12+
private final String credentialsFilePath;
13+
private final String tokenCustomUrl;
14+
private final String tokenExpirationLeeway;
15+
16+
Configuration(Builder builder) {
17+
this.defaultHeader = builder.defaultHeader;
18+
this.serviceAccountKey = builder.serviceAccountKey;
19+
this.serviceAccountKeyPath = builder.serviceAccountKeyPath;
20+
this.privateKeyPath = builder.privateKeyPath;
21+
this.privateKey = builder.privateKey;
22+
this.customEndpoint = builder.customEndpoint;
23+
this.credentialsFilePath = builder.credentialsFilePath;
24+
this.tokenCustomUrl = builder.tokenCustomUrl;
25+
this.tokenExpirationLeeway = builder.tokenExpirationLeeway;
26+
}
27+
28+
public Map<String, String> getDefaultHeader() {
29+
return defaultHeader;
30+
}
31+
32+
public String getServiceAccountKey() {
33+
return serviceAccountKey;
34+
}
35+
36+
public String getServiceAccountKeyPath() {
37+
return serviceAccountKeyPath;
38+
}
39+
40+
public String getPrivateKeyPath() {
41+
return privateKeyPath;
42+
}
43+
44+
public String getPrivateKey() {
45+
return privateKey;
46+
}
47+
48+
public String getCustomEndpoint() {
49+
return customEndpoint;
50+
}
51+
52+
public String getCredentialsFilePath() {
53+
return credentialsFilePath;
54+
}
55+
56+
public String getTokenCustomUrl() {
57+
return tokenCustomUrl;
58+
}
59+
60+
public String getTokenExpirationLeeway() {
61+
return tokenExpirationLeeway;
62+
}
63+
64+
public static class Builder {
65+
private Map<String, String> defaultHeader;
66+
private String serviceAccountKey;
67+
private String serviceAccountKeyPath;
68+
private String privateKeyPath;
69+
private String privateKey;
70+
private String customEndpoint;
71+
private String credentialsFilePath;
72+
private String tokenCustomUrl;
73+
private String tokenExpirationLeeway;
74+
75+
public Builder defaultHeader(Map<String, String> defaultHeader) {
76+
this.defaultHeader = defaultHeader;
77+
return this;
78+
}
79+
80+
public Builder serviceAccountKey(String serviceAccountKey) {
81+
this.serviceAccountKey = serviceAccountKey;
82+
return this;
83+
}
84+
85+
public Builder serviceAccountKeyPath(String serviceAccountKeyPath) {
86+
this.serviceAccountKeyPath = serviceAccountKeyPath;
87+
return this;
88+
}
89+
90+
public Builder privateKeyPath(String privateKeyPath) {
91+
this.privateKeyPath = privateKeyPath;
92+
return this;
93+
}
94+
95+
public Builder privateKey(String privateKey) {
96+
this.privateKey = privateKey;
97+
return this;
98+
}
99+
100+
public Builder customEndpoint(String customEndpoint) {
101+
this.customEndpoint = customEndpoint;
102+
return this;
103+
}
104+
105+
public Builder credentialsFilePath(String credentialsFilePath) {
106+
this.credentialsFilePath = credentialsFilePath;
107+
return this;
108+
}
109+
110+
public Builder tokenCustomUrl(String tokenCustomUrl) {
111+
this.tokenCustomUrl = tokenCustomUrl;
112+
return this;
113+
}
114+
115+
public Builder tokenExpirationLeeway(String tokenExpirationLeeway) {
116+
this.tokenExpirationLeeway = tokenExpirationLeeway;
117+
return this;
118+
}
119+
120+
public Configuration build() {
121+
return new Configuration(this);
122+
}
123+
}
124+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package cloud.stackit.sdk.core.config;
2+
3+
public class EnvironmentVariables {
4+
public final static String ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH = "STACKIT_SERVICE_ACCOUNT_KEY_PATH";
5+
public final static String ENV_STACKIT_SERVICE_ACCOUNT_KEY = "STACKIT_SERVICE_ACCOUNT_KEY";
6+
public final static String ENV_STACKIT_PRIVATE_KEY_PATH = "STACKIT_PRIVATE_KEY_PATH";
7+
public final static String ENV_STACKIT_PRIVATE_KEY = "STACKIT_PRIVATE_KEY";
8+
public final static String ENV_STACKIT_TOKEN_BASEURL = "STACKIT_TOKEN_BASEURL";
9+
public final static String ENV_STACKIT_CREDENTIALS_PATH = "STACKIT_CREDENTIALS_PATH";
10+
11+
public final static String STACKIT_SERVICE_ACCOUNT_KEY_PATH = System.getenv(ENV_STACKIT_SERVICE_ACCOUNT_KEY_PATH);
12+
public final static String STACKIT_SERVICE_ACCOUNT_KEY = System.getenv(ENV_STACKIT_SERVICE_ACCOUNT_KEY);
13+
public final static String STACKIT_PRIVATE_KEY_PATH = System.getenv(ENV_STACKIT_PRIVATE_KEY_PATH);
14+
public final static String STACKIT_PRIVATE_KEY = System.getenv(ENV_STACKIT_PRIVATE_KEY);
15+
public final static String STACKIT_TOKEN_BASEURL = System.getenv(ENV_STACKIT_TOKEN_BASEURL);
16+
public final static String STACKIT_CREDENTIALS_PATH = System.getenv(ENV_STACKIT_CREDENTIALS_PATH);
17+
18+
19+
@Override
20+
public String toString() {
21+
return "EnvironmentVariables{" +
22+
"STACKIT_SERVICE_ACCOUNT_KEY_PATH='" + STACKIT_SERVICE_ACCOUNT_KEY_PATH + '\'' +
23+
", STACKIT_SERVICE_ACCOUNT_KEY='" + STACKIT_SERVICE_ACCOUNT_KEY + '\'' +
24+
", STACKIT_PRIVATE_KEY_PATH='" + STACKIT_PRIVATE_KEY_PATH + '\'' +
25+
", STACKIT_PRIVATE_KEY='" + STACKIT_PRIVATE_KEY + '\'' +
26+
", STACKIT_TOKEN_BASEURL='" + STACKIT_TOKEN_BASEURL + '\'' +
27+
", STACKIT_CREDENTIALS_PATH='" + STACKIT_CREDENTIALS_PATH + '\'' +
28+
'}';
29+
}
30+
}

core/src/main/java/cloud/stackit/sdk/core/model/ServiceAccountCredentials.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.security.KeyFactory;
44
import java.security.NoSuchAlgorithmException;
5-
import java.security.PrivateKey;
65
import java.security.interfaces.RSAPrivateKey;
76
import java.security.spec.InvalidKeySpecException;
87
import java.security.spec.PKCS8EncodedKeySpec;
@@ -13,7 +12,7 @@ public class ServiceAccountCredentials {
1312
private final String aud;
1413
private final String iss;
1514
private final String kid;
16-
private final String privateKey;
15+
private String privateKey;
1716
private final UUID sub;
1817

1918
public ServiceAccountCredentials(String aud, String iss, String kid, String privateKey, UUID sub) {
@@ -40,6 +39,14 @@ public String getPrivateKey() {
4039
return privateKey;
4140
}
4241

42+
public void setPrivateKey(String privateKey) {
43+
this.privateKey = privateKey;
44+
}
45+
46+
public boolean isPrivateKeySet() {
47+
return !privateKey.trim().isEmpty();
48+
}
49+
4350
public UUID getSub() {
4451
return sub;
4552
}

0 commit comments

Comments
 (0)