Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a97adca
Introducing Common JWT support for Pod APIs
symphony-mariacristina Jan 4, 2022
26f5b87
Adding more unit test covererage- Still WIP
symphony-mariacristina Jan 4, 2022
f17f8ef
Add test for BearerEnabledApiClient
symphony-mariacristina Jan 4, 2022
76ee625
Update symphony-bdk-core/src/main/java/com/symphony/bdk/core/auth/Aut…
symphony-mariacristina Jan 4, 2022
c11fb88
Throwing exception if jwt parsing fails during refresh + unit tests p…
symphony-mariacristina Jan 5, 2022
3ef6f53
Merge branch 'commonJwtPod' of https://github.com/symphony-mariacrist…
symphony-mariacristina Jan 5, 2022
4ff1b00
Saving jwt expiration date as part of the AuthSession + adding leeway…
symphony-mariacristina Jan 5, 2022
a23b6a3
Merge branch 'main' of https://github.com/finos/symphony-bdk-java int…
symphony-mariacristina Jan 5, 2022
043f037
removing authSession dependency in the ApiClientFactory + applying ch…
symphony-mariacristina Jan 11, 2022
cd81c07
removing unused imports
symphony-mariacristina Jan 11, 2022
6402f83
Adding javadoc + coverage for AuthSession
symphony-mariacristina Jan 11, 2022
9c5d6a3
Refactoring removed BearerEnabledApiClient part 1
symphony-mariacristina Jan 11, 2022
e521a07
Adding more test coverage
symphony-mariacristina Jan 11, 2022
50ff111
missing addEnforcedAuthenticationScheme implementation added
symphony-mariacristina Jan 11, 2022
457877f
add test in BdkCoreConfigTest
symphony-mariacristina Jan 11, 2022
5a1bb3e
Allowing refresh of bearer token before making the api call
symphony-mariacristina Jan 12, 2022
44671cf
Making the bot fail if the SBE version do not support yet the common jwt
symphony-mariacristina Jan 12, 2022
c7cd1cf
refactoring after review comments
symphony-mariacristina Jan 12, 2022
038daed
BdkCoreConfig should not add authentication to podClient if oboMode o…
symphony-mariacristina Jan 12, 2022
3bfd613
Addressing review comments
symphony-mariacristina Jan 12, 2022
9f25364
ServiceFactory should not add authentication to podClient if oboMod
symphony-mariacristina Jan 12, 2022
e045ef6
Update symphony-bdk-core/src/main/java/com/symphony/bdk/core/auth/Aut…
symphony-mariacristina Jan 13, 2022
f1bf053
Update symphony-bdk-core/src/main/java/com/symphony/bdk/core/auth/OAu…
symphony-mariacristina Jan 13, 2022
ef2ecb3
Allowing a config with both bot+app to use common jwt when bot is mak…
symphony-mariacristina Jan 13, 2022
f5fae59
Merge branch 'commonJwtPod' of https://github.com/symphony-mariacrist…
symphony-mariacristina Jan 13, 2022
ae4de1b
Adding null checks while extracting the common jwt expiration date
symphony-mariacristina Jan 13, 2022
9c34349
Making the BDK fail if app configuration is found
symphony-mariacristina Jan 13, 2022
6054612
Improving tokens refresh
symphony-mariacristina Jan 13, 2022
ce27a1c
Adding more unit test coverage
symphony-mariacristina Jan 14, 2022
24fead9
missing javadoc + small typo
symphony-mariacristina Jan 14, 2022
41eae29
Return error message when common jwt feature is not available on SBE
symphony-mariacristina Jan 14, 2022
e0af94c
documenting new configuration property
symphony-mariacristina Jan 14, 2022
10fe1d3
Renaming,reorganizing and commenting
symphony-youri Jan 14, 2022
db7452a
Better test coverage
symphony-youri Jan 14, 2022
3f0f859
Removing flag from doc since we still have not implemented yet fully …
symphony-mariacristina Jan 14, 2022
55a5303
Merge branch 'commonJwtPod' of https://github.com/symphony-mariacrist…
symphony-mariacristina Jan 14, 2022
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
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ datafeed:
initialIntervalMillis: 2000
multiplier: 1.5
maxIntervalMillis: 10000

retry:
maxAttempts: 6 # set '-1' for an infinite number of attempts, default value is '10'
initialIntervalMillis: 2000
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.symphony.bdk.core;

import static com.symphony.bdk.core.auth.impl.OAuthentication.BEARER_AUTH;

import com.symphony.bdk.core.auth.AuthSession;
import com.symphony.bdk.core.auth.impl.OAuthSession;
import com.symphony.bdk.core.auth.impl.OAuthentication;
import com.symphony.bdk.core.client.ApiClientFactory;
import com.symphony.bdk.core.config.model.BdkConfig;
import com.symphony.bdk.core.retry.RetryWithRecoveryBuilder;
Expand Down Expand Up @@ -43,6 +47,7 @@
import com.symphony.bdk.http.api.ApiClient;
import com.symphony.bdk.template.api.TemplateEngine;

import lombok.extern.slf4j.Slf4j;
import org.apiguardian.api.API;

/**
Expand All @@ -56,6 +61,7 @@
* <li>{@link SessionService}</li>
* </ul>
*/
@Slf4j
@API(status = API.Status.INTERNAL)
class ServiceFactory {

Expand All @@ -68,13 +74,24 @@ class ServiceFactory {
private final RetryWithRecoveryBuilder<?> retryBuilder;

public ServiceFactory(ApiClientFactory apiClientFactory, AuthSession authSession, BdkConfig config) {
this.config = config;
this.podClient = apiClientFactory.getPodClient();
this.agentClient = apiClientFactory.getAgentClient();
this.datafeedAgentClient = apiClientFactory.getDatafeedAgentClient();
this.authSession = authSession;
this.templateEngine = TemplateEngine.getDefaultImplementation();
this.config = config;
this.retryBuilder = new RetryWithRecoveryBuilder<>().retryConfig(config.getRetry());

if (config.isCommonJwtEnabled()) {
if (config.isOboConfigured()) {
throw new UnsupportedOperationException("Common JWT feature is not available yet in OBO mode,"
+ " please set commonJwt.enabled to false.");
} else {
final OAuthSession oAuthSession = new OAuthSession(authSession);
this.podClient.getAuthentications().put(BEARER_AUTH, new OAuthentication(oAuthSession::getBearerToken));
this.podClient.addEnforcedAuthenticationScheme(BEARER_AUTH);
}
}
}

/**
Expand All @@ -83,7 +100,8 @@ public ServiceFactory(ApiClientFactory apiClientFactory, AuthSession authSession
* @return a new {@link UserService} instance.
*/
public UserService getUserService() {
return new UserService(new UserApi(podClient), new UsersApi(podClient), new AuditTrailApi(agentClient), authSession, retryBuilder);
return new UserService(new UserApi(podClient), new UsersApi(podClient), new AuditTrailApi(agentClient), authSession,
retryBuilder);
}

/**
Expand Down Expand Up @@ -187,4 +205,5 @@ public ApplicationService getApplicationService() {
public HealthService getHealthService() {
return new HealthService(new SystemApi(this.agentClient), new SignalsApi(this.agentClient), this.authSession);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ public interface AuthSession {
*/
@Nullable String getSessionToken();

/**
* Pod's Common JWT authentication token. When commonJwt.enabled is set to true in the configuration, an OAuth
* authentication scheme is used where the session token acts as the refresh token and the authorization token is a
* short lived access token.
*
* @return the Pod Authorization token
*/
@Nullable default String getAuthorizationToken() throws AuthUnauthorizedException {
return null;
}

/**
* KeyManager's authentication token.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ BotAuthenticator getBotAuthenticator() throws AuthInitializationException {
return new BotAuthenticatorCertImpl(
this.config.getRetry(),
this.config.getBot().getUsername(),
this.config.getCommonJwt(),
this.apiClientFactory.getLoginClient(),
this.apiClientFactory.getSessionAuthClient(),
this.apiClientFactory.getKeyAuthClient()
);
Expand All @@ -86,6 +88,7 @@ BotAuthenticator getBotAuthenticator() throws AuthInitializationException {
return new BotAuthenticatorRsaImpl(
this.config.getRetry(),
this.config.getBot().getUsername(),
this.config.getCommonJwt(),
this.loadPrivateKeyFromAuthenticationConfig(this.config.getBot()),
this.apiClientFactory.getLoginClient(),
this.apiClientFactory.getRelayClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
@API(status = API.Status.STABLE)
public class AuthUnauthorizedException extends Exception {

public AuthUnauthorizedException(@Nonnull String message) {
super(message);
}

public AuthUnauthorizedException(@Nonnull String message, @Nonnull ApiException source) {
super(message, source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,86 @@

import com.symphony.bdk.core.auth.BotAuthenticator;
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
import com.symphony.bdk.core.config.model.BdkCommonJwtConfig;
import com.symphony.bdk.core.config.model.BdkRetryConfig;
import com.symphony.bdk.gen.api.AuthenticationApi;
import com.symphony.bdk.gen.api.model.JwtToken;
import com.symphony.bdk.gen.api.model.Token;
import com.symphony.bdk.http.api.ApiClient;
import com.symphony.bdk.http.api.ApiException;

import lombok.extern.slf4j.Slf4j;
import org.apiguardian.api.API;

import javax.annotation.Nonnull;

/**
* Abstract class to factorize the {@link BotAuthenticator} logic between RSA and certificate,
* especially the retry logic on top of HTTP calls.
*/
@Slf4j
@API(status = API.Status.INTERNAL)
public abstract class AbstractBotAuthenticator implements BotAuthenticator {

private final AuthenticationRetry<String> authenticationRetry;
protected final ApiClient loginApiClient;
private final BdkCommonJwtConfig commonJwtConfig;

private final AuthenticationRetry<String> kmAuthenticationRetry;
private final AuthenticationRetry<Token> podAuthenticationRetry;
private final AuthenticationRetry<JwtToken> idmAuthenticationRetry;

public AbstractBotAuthenticator(BdkRetryConfig retryConfig) {
authenticationRetry = new AuthenticationRetry<>(retryConfig);
protected AbstractBotAuthenticator(BdkRetryConfig retryConfig,
@Nonnull BdkCommonJwtConfig commonJwtConfig, @Nonnull ApiClient loginApiClient) {
kmAuthenticationRetry = new AuthenticationRetry<>(retryConfig);
podAuthenticationRetry = new AuthenticationRetry<>(retryConfig);
idmAuthenticationRetry = new AuthenticationRetry<>(retryConfig);
this.commonJwtConfig = commonJwtConfig;
this.loginApiClient = loginApiClient;
}

protected String retrieveToken(ApiClient client) throws AuthUnauthorizedException {
protected abstract String retrieveKeyManagerToken() throws AuthUnauthorizedException;

protected String retrieveKeyManagerToken(ApiClient client) throws AuthUnauthorizedException {
final String unauthorizedMessage = String.format("Service account \"%s\" is not authorized to authenticate. "
+ "Check if credentials are valid.", getBotUsername());

return kmAuthenticationRetry.executeAndRetry("AbstractBotAuthenticator.retrieveKeyManagerToken",
client.getBasePath(), () -> doRetrieveToken(client).getToken(), unauthorizedMessage);
}

protected abstract Token retrieveSessionToken() throws AuthUnauthorizedException;

protected Token retrieveSessionToken(ApiClient client) throws AuthUnauthorizedException {
final String unauthorizedMessage = String.format("Service account \"%s\" is not authorized to authenticate. "
+ "Check if credentials are valid.", getBotUsername());

return authenticationRetry.executeAndRetry("AbstractBotAuthenticator.retrieveToken", client.getBasePath(),
() -> authenticateAndGetToken(client), unauthorizedMessage);
return podAuthenticationRetry.executeAndRetry("AbstractBotAuthenticator.retrieveSessionToken",
client.getBasePath(), () -> this.doRetrieveToken(client), unauthorizedMessage);
}

protected abstract String authenticateAndGetToken(ApiClient client) throws ApiException;
/**
* Login API to retrieve a token is the same for KM and pod.
*/
protected abstract Token doRetrieveToken(ApiClient client) throws ApiException;

protected String retrieveAuthorizationToken(String sessionToken) throws AuthUnauthorizedException {
log.debug("Start retrieving authorizationToken using RSA authentication...");
return this.doRetrieveAuthorizationToken(this.loginApiClient, sessionToken).getAccessToken();
}

private JwtToken doRetrieveAuthorizationToken(ApiClient client, String sessionToken)
throws AuthUnauthorizedException {
final String unauthorizedMessage = String.format("Service account \"%s\" is not authorized to authenticate. "
+ "Check if credentials are valid.", getBotUsername());

// we are not using any scopes for now when calling pod APIs
return idmAuthenticationRetry.executeAndRetry("AbstractBotAuthenticator.retrieveAuthorizationToken",
client.getBasePath(), () -> new AuthenticationApi(client).idmTokensPost(sessionToken, ""), unauthorizedMessage);
}

protected abstract String getBotUsername();

public boolean isCommonJwtEnabled() {
return commonJwtConfig.getEnabled();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public abstract class AbstractOboAuthenticator implements OboAuthenticator {
protected final String appId;
private final AuthenticationRetry<String> authenticationRetry;

public AbstractOboAuthenticator(BdkRetryConfig retryConfig, String appId) {
protected AbstractOboAuthenticator(BdkRetryConfig retryConfig, String appId) {
this.appId = appId;
this.authenticationRetry = new AuthenticationRetry<>(retryConfig);
}
Expand Down

This file was deleted.

Loading