Skip to content

Commit 13e6d25

Browse files
authored
Introduced AuthenticatorFactory interface (#832)
1 parent b3e8bdd commit 13e6d25

7 files changed

Lines changed: 297 additions & 253 deletions

File tree

symphony-bdk-core/src/main/java/com/symphony/bdk/core/SymphonyBdk.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.symphony.bdk.core;
22

33
import com.symphony.bdk.core.activity.ActivityRegistry;
4-
import com.symphony.bdk.core.auth.AuthSession;
5-
import com.symphony.bdk.core.auth.AuthenticatorFactory;
6-
import com.symphony.bdk.core.auth.ExtensionAppAuthenticator;
7-
import com.symphony.bdk.core.auth.OboAuthenticator;
4+
import com.symphony.bdk.core.auth.*;
85
import com.symphony.bdk.core.auth.exception.AuthInitializationException;
96
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
7+
import com.symphony.bdk.core.auth.impl.AuthenticatorFactoryImpl;
108
import com.symphony.bdk.core.client.ApiClientFactory;
119
import com.symphony.bdk.core.config.exception.BotNotConfiguredException;
1210
import com.symphony.bdk.core.config.model.BdkConfig;
@@ -103,7 +101,7 @@ protected SymphonyBdk(
103101
}
104102

105103
if (authenticatorFactory == null) {
106-
authenticatorFactory = new AuthenticatorFactory(this.config, apiClientFactory);
104+
authenticatorFactory = new AuthenticatorFactoryImpl(this.config, apiClientFactory);
107105
}
108106

109107
this.oboAuthenticator = config.isOboConfigured() ? authenticatorFactory.getOboAuthenticator() : null;

symphony-bdk-core/src/main/java/com/symphony/bdk/core/SymphonyBdkBuilder.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.symphony.bdk.core;
22

33
import com.symphony.bdk.core.auth.AuthenticatorFactory;
4+
import com.symphony.bdk.core.auth.impl.AuthenticatorFactoryImpl;
45
import com.symphony.bdk.core.auth.exception.AuthInitializationException;
56
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
67
import com.symphony.bdk.core.client.ApiClientFactory;
@@ -22,7 +23,7 @@
2223
* Fluent builder for advanced configuration of the {@link SymphonyBdk} entry point.
2324
*
2425
* <p>Please note that some of the parameters (such as {@link ApiClientBuilderProvider}, {@link ApiClientFactory} or
25-
* {@link AuthenticatorFactory}) have to be used with caution.
26+
* {@link AuthenticatorFactoryImpl}) have to be used with caution.
2627
*/
2728
@Generated
2829
@API(status = API.Status.EXPERIMENTAL)
@@ -72,9 +73,9 @@ public SymphonyBdkBuilder apiClientFactory(@Nullable ApiClientFactory apiClientF
7273
}
7374

7475
/**
75-
* With custom {@link AuthenticatorFactory} instance.
76+
* With custom {@link AuthenticatorFactoryImpl} instance.
7677
*
77-
* @param authenticatorFactory a custom {@link AuthenticatorFactory} instance.
78+
* @param authenticatorFactory a custom {@link AuthenticatorFactoryImpl} instance.
7879
* @return updated builder.
7980
*/
8081
public SymphonyBdkBuilder authenticatorFactory(@Nullable AuthenticatorFactory authenticatorFactory) {
@@ -115,7 +116,7 @@ public SymphonyBdk build() throws AuthUnauthorizedException, AuthInitializationE
115116
}
116117

117118
if (this.authenticatorFactory == null) {
118-
this.authenticatorFactory = new AuthenticatorFactory(this.config, this.apiClientFactory);
119+
this.authenticatorFactory = new AuthenticatorFactoryImpl(this.config, this.apiClientFactory);
119120
}
120121

121122
final SymphonyBdk bdk = new SymphonyBdk(this.config, this.apiClientFactory, this.authenticatorFactory);
Lines changed: 17 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -1,237 +1,46 @@
11
package com.symphony.bdk.core.auth;
22

3-
import static com.symphony.bdk.core.util.DeprecationLogger.logDeprecation;
4-
import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
5-
63
import com.symphony.bdk.core.auth.exception.AuthInitializationException;
7-
import com.symphony.bdk.core.auth.impl.BotAuthenticatorCertImpl;
8-
import com.symphony.bdk.core.auth.impl.BotAuthenticatorRsaImpl;
9-
import com.symphony.bdk.core.auth.impl.ExtensionAppAuthenticatorCertImpl;
10-
import com.symphony.bdk.core.auth.impl.ExtensionAppAuthenticatorRsaImpl;
11-
import com.symphony.bdk.core.auth.impl.InMemoryTokensRepository;
12-
import com.symphony.bdk.core.auth.impl.OboAuthenticatorCertImpl;
13-
import com.symphony.bdk.core.auth.impl.OboAuthenticatorRsaImpl;
14-
import com.symphony.bdk.core.auth.jwt.JwtHelper;
15-
import com.symphony.bdk.core.client.ApiClientFactory;
16-
import com.symphony.bdk.core.config.model.BdkAuthenticationConfig;
17-
import com.symphony.bdk.core.config.model.BdkConfig;
18-
19-
import com.symphony.bdk.core.service.version.AgentVersionService;
20-
21-
import com.symphony.bdk.gen.api.SignalsApi;
224

23-
import lombok.extern.slf4j.Slf4j;
24-
import org.apache.commons.io.IOUtils;
255
import org.apiguardian.api.API;
266

27-
import java.io.FileInputStream;
28-
import java.io.IOException;
29-
import java.io.InputStream;
30-
import java.nio.charset.StandardCharsets;
31-
import java.security.GeneralSecurityException;
32-
import java.security.PrivateKey;
33-
347
import javax.annotation.Nonnull;
358

369
/**
37-
* Factory class that provides new instances for the main authenticators :
10+
* Factory responsible for creating different authenticators.
3811
* <ul>
39-
* <li>{@link BotAuthenticator} : to authenticate the main Bot service account</li>
40-
* <li>{@link OboAuthenticator} : to perform on-behalf-of authentication</li>
12+
* <li>Bot Authenticator: for authenticating the main bot service account</li>
13+
* <li>OBO Authenticator: for authenticating on behalf of a regular Symphony user</li>
14+
* <li>Extension App Authenticator: for authenticating an extension application</li>
4115
* </ul>
4216
*/
43-
@Slf4j
4417
@API(status = API.Status.STABLE)
45-
public class AuthenticatorFactory {
46-
47-
private final BdkConfig config;
48-
private final ApiClientFactory apiClientFactory;
49-
private final ExtensionAppTokensRepository extensionAppTokensRepository;
50-
51-
public AuthenticatorFactory(@Nonnull BdkConfig bdkConfig, @Nonnull ApiClientFactory apiClientFactory) {
52-
this(bdkConfig, apiClientFactory, new InMemoryTokensRepository());
53-
}
54-
55-
public AuthenticatorFactory(@Nonnull BdkConfig bdkConfig, @Nonnull ApiClientFactory apiClientFactory,
56-
@Nonnull ExtensionAppTokensRepository extensionAppTokensRepository) {
57-
this.config = bdkConfig;
58-
this.apiClientFactory = apiClientFactory;
59-
this.extensionAppTokensRepository = extensionAppTokensRepository;
60-
}
18+
public interface AuthenticatorFactory {
6119

6220
/**
63-
* Creates a new instance of a {@link BotAuthenticator} service.
21+
* Creates a new instance of a {@link BotAuthenticator}.
6422
*
6523
* @return a new {@link BotAuthenticator} instance.
24+
* @throws AuthInitializationException if the authenticator cannot be instantiated.
6625
*/
67-
public @Nonnull
68-
BotAuthenticator getBotAuthenticator() throws AuthInitializationException {
69-
if (this.config.getBot().isBothCertificateAndRsaConfigured()) {
70-
throw new AuthInitializationException(
71-
"Both of certificate and rsa authentication are configured. Only one of them should be provided.");
72-
}
73-
if (this.config.getBot().isCertificateAuthenticationConfigured()) {
74-
if (!this.config.getBot().isCertificateConfigurationValid()) {
75-
throw new AuthInitializationException(
76-
"Only one of certificate path or content should be configured for bot authentication.");
77-
}
78-
return new BotAuthenticatorCertImpl(
79-
this.config.getRetry(),
80-
this.config.getBot().getUsername(),
81-
this.config.getCommonJwt(),
82-
this.apiClientFactory.getLoginClient(),
83-
this.apiClientFactory.getSessionAuthClient(),
84-
this.apiClientFactory.getKeyAuthClient(),
85-
new AgentVersionService(new SignalsApi(this.apiClientFactory.getAgentClient()))
86-
);
87-
}
88-
if (this.config.getBot().isRsaAuthenticationConfigured()) {
89-
if (!this.config.getBot().isRsaConfigurationValid()) {
90-
throw new AuthInitializationException(
91-
"Only one of private key path or content should be configured for bot authentication.");
92-
}
93-
return new BotAuthenticatorRsaImpl(
94-
this.config.getRetry(),
95-
this.config.getBot().getUsername(),
96-
this.config.getCommonJwt(),
97-
this.loadPrivateKeyFromAuthenticationConfig(this.config.getBot()),
98-
this.apiClientFactory.getLoginClient(),
99-
this.apiClientFactory.getRelayClient(),
100-
new AgentVersionService(new SignalsApi(this.apiClientFactory.getAgentClient()))
101-
);
102-
}
103-
throw new AuthInitializationException("Neither RSA private key nor certificate is configured.");
104-
}
26+
@Nonnull
27+
BotAuthenticator getBotAuthenticator() throws AuthInitializationException;
10528

10629
/**
107-
* Creates a new instance of an {@link OboAuthenticator} service.
30+
* Creates a new instance of a {@link OboAuthenticator}.
10831
*
10932
* @return a new {@link OboAuthenticator} instance.
33+
* @throws AuthInitializationException if the authenticator cannot be instantiated.
11034
*/
111-
public @Nonnull
112-
OboAuthenticator getOboAuthenticator() throws AuthInitializationException {
113-
if (this.config.getApp().isBothCertificateAndRsaConfigured()) {
114-
throw new AuthInitializationException(
115-
"Both of certificate and rsa authentication are configured. Only one of them should be provided.");
116-
}
117-
if (this.config.getApp().isCertificateAuthenticationConfigured()) {
118-
if (!this.config.getApp().isCertificateConfigurationValid()) {
119-
throw new AuthInitializationException(
120-
"Only one of certificate path or content should be configured for app authentication.");
121-
}
122-
return new OboAuthenticatorCertImpl(
123-
this.config.getRetry(),
124-
this.config.getApp().getAppId(),
125-
this.apiClientFactory.getExtAppSessionAuthClient()
126-
);
127-
}
128-
if (this.config.getApp().isRsaAuthenticationConfigured()) {
129-
if (!this.config.getApp().isRsaConfigurationValid()) {
130-
throw new AuthInitializationException(
131-
"Only one of private key path or content should be configured for app authentication.");
132-
}
133-
return new OboAuthenticatorRsaImpl(
134-
this.config.getRetry(),
135-
this.config.getApp().getAppId(),
136-
this.loadPrivateKeyFromAuthenticationConfig(this.config.getApp()),
137-
this.apiClientFactory.getLoginClient()
138-
);
139-
}
140-
throw new AuthInitializationException("Neither RSA private key nor certificate is configured.");
141-
}
35+
@Nonnull
36+
OboAuthenticator getOboAuthenticator() throws AuthInitializationException;
14237

14338
/**
144-
* Creates a new instance of an {@link ExtensionAppAuthenticator} service.
39+
* Creates a new instance of a {@link ExtensionAppAuthenticator}.
14540
*
14641
* @return a new {@link ExtensionAppAuthenticator} instance.
42+
* @throws AuthInitializationException if the authenticator cannot be instantiated.
14743
*/
148-
public @Nonnull
149-
ExtensionAppAuthenticator getExtensionAppAuthenticator() throws AuthInitializationException {
150-
if (this.config.getApp().isBothCertificateAndRsaConfigured()) {
151-
throw new AuthInitializationException(
152-
"Both of certificate and rsa authentication are configured. Only one of them should be provided.");
153-
}
154-
if (this.config.getApp().isCertificateAuthenticationConfigured()) {
155-
if (!this.config.getApp().isCertificateConfigurationValid()) {
156-
throw new AuthInitializationException(
157-
"Only one of certificate path or content should be configured for app authentication.");
158-
}
159-
return new ExtensionAppAuthenticatorCertImpl(
160-
this.config.getRetry(),
161-
this.config.getApp().getAppId(),
162-
this.apiClientFactory.getExtAppSessionAuthClient(),
163-
extensionAppTokensRepository);
164-
}
165-
if (this.config.getApp().isRsaAuthenticationConfigured()) {
166-
if (!this.config.getApp().isRsaConfigurationValid()) {
167-
throw new AuthInitializationException(
168-
"Only one of private key path or content should be configured for app authentication.");
169-
}
170-
return new ExtensionAppAuthenticatorRsaImpl(
171-
this.config.getRetry(),
172-
this.config.getApp().getAppId(),
173-
this.loadPrivateKeyFromAuthenticationConfig(this.config.getApp()),
174-
this.apiClientFactory.getLoginClient(),
175-
this.apiClientFactory.getPodClient(),
176-
extensionAppTokensRepository
177-
);
178-
}
179-
throw new AuthInitializationException("Neither RSA private key nor certificate is configured.");
180-
}
181-
182-
private PrivateKey loadPrivateKeyFromAuthenticationConfig(BdkAuthenticationConfig config)
183-
throws AuthInitializationException {
184-
String privateKeyPath = "";
185-
try {
186-
String privateKey;
187-
if (config.getPrivateKey() != null && config.getPrivateKey().isConfigured()) {
188-
if (isNotEmpty(config.getPrivateKey().getContent())) {
189-
privateKey = new String(config.getPrivateKey().getContent(), StandardCharsets.UTF_8);
190-
} else {
191-
privateKeyPath = config.getPrivateKey().getPath();
192-
log.debug("Loading RSA privateKey from path : {}", privateKeyPath);
193-
privateKey = loadPrivateKey(privateKeyPath);
194-
}
195-
} else {
196-
logDeprecation("RSA private key should be configured under \"privateKey\" field");
197-
if (isNotEmpty(config.getPrivateKeyContent())) {
198-
privateKey = new String(config.getPrivateKeyContent(), StandardCharsets.UTF_8);
199-
} else {
200-
privateKeyPath = config.getPrivateKeyPath();
201-
log.debug("Loading RSA privateKey from path : {}", privateKeyPath);
202-
privateKey = loadPrivateKey(privateKeyPath);
203-
}
204-
}
205-
return JwtHelper.parseRsaPrivateKey(privateKey);
206-
} catch (GeneralSecurityException e) {
207-
final String message = String.format("Unable to parse RSA Private Key from path %s. Check if the format is "
208-
+ "correct.", privateKeyPath);
209-
throw new AuthInitializationException(message, e);
210-
} catch (IOException e) {
211-
final String message = "Unable to read or find RSA Private Key from path " + privateKeyPath;
212-
throw new AuthInitializationException(message, e);
213-
}
214-
}
215-
216-
private static String loadPrivateKey(String privateKeyPath) throws IOException, AuthInitializationException {
217-
InputStream is;
218-
219-
// useful for testing when private key is located into resources
220-
if (privateKeyPath.startsWith("classpath:")) {
221-
log.warn("Warning: Keeping RSA private keys into project resources is dangerous. "
222-
+ "You should consider another location for production.");
223-
is = AuthenticatorFactory.class.getResourceAsStream(privateKeyPath.replace("classpath:", ""));
224-
if (is == null) {
225-
throw new AuthInitializationException(
226-
"Unable to find RSA private key as classpath resource from: " + privateKeyPath);
227-
}
228-
try (InputStream resourceStream = is) {
229-
return IOUtils.toString(resourceStream, StandardCharsets.UTF_8);
230-
}
231-
} else {
232-
try (InputStream fileStream = new FileInputStream(privateKeyPath)) {
233-
return IOUtils.toString(fileStream, StandardCharsets.UTF_8);
234-
}
235-
}
236-
}
44+
@Nonnull
45+
ExtensionAppAuthenticator getExtensionAppAuthenticator() throws AuthInitializationException;
23746
}

0 commit comments

Comments
 (0)