Skip to content
Open
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
2 changes: 1 addition & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ repositories {
}

dependencies {
implementation 'org.openapitools:openapi-generator-gradle-plugin:5.4.0'
implementation 'org.openapitools:openapi-generator-gradle-plugin:6.6.0'
implementation 'de.undercouch:gradle-download-task:5.0.2'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.42.0'
}
3 changes: 2 additions & 1 deletion symphony-bdk-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,14 @@ dependencies {
}

// OpenAPI code generation
def apiBaseUrl = "https://raw.githubusercontent.com/finos/symphony-api-spec/fc80c3204d8a92a0b82d3c951eab7f5cb78a7c53"
def apiBaseUrl = "https://raw.githubusercontent.com/aidenM-symphony/symphony-api-spec/6ff7cc838ff20a57535f0ff16833047b08587072/"
def generatedFolder = "$buildDir/generated/openapi"
def apisToGenerate = [
Agent: 'agent/agent-api-public-deprecated.yaml',
Pod : 'pod/pod-api-public-deprecated.yaml',
Auth : 'authenticator/authenticator-api-public-deprecated.yaml',
Login: 'login/login-api-public.yaml',
Users: 'users/users-api-public.yaml'
]

sourceSets.main.java.srcDirs += "$generatedFolder/src/main/java"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.symphony.bdk.core;

import com.symphony.bdk.core.auth.ExtAppAuthSession;
import com.symphony.bdk.core.retry.RetryWithRecoveryBuilder;
import com.symphony.bdk.core.service.apps.AppService;

import com.symphony.bdk.gen.api.AppsApi;
import com.symphony.bdk.http.api.ApiClient;

import org.apiguardian.api.API;

@API(status = API.Status.EXPERIMENTAL)
public class ExtAppServices {
AppService appService;

public ExtAppServices(ApiClient apiClient, ExtAppAuthSession authSession, RetryWithRecoveryBuilder<?> retryBuilder) {
AppsApi appsApi = new AppsApi(apiClient);
this.appService = new AppService(appsApi, authSession, retryBuilder);
}

public AppService app() {
return this.appService;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class SymphonyBdk {
private final ExtensionAppAuthenticator extensionAppAuthenticator;

private final AuthSession botSession;
private final ExtAppAuthSession extAppAuthSession;
private final UserV2 botInfo;
private final DatafeedLoop datafeedLoop;
private final DatahoseLoop datahoseLoop;
Expand All @@ -63,6 +64,8 @@ public class SymphonyBdk {
private final SessionService sessionService;
private final HealthService healthService;
private final ExtensionService extensionService;
private final ExtAppServices extAppServices;


/**
* Returns a new {@link SymphonyBdkBuilder} for fluent initialization.
Expand Down Expand Up @@ -128,6 +131,17 @@ protected SymphonyBdk(
this.messageService = serviceFactory != null ? serviceFactory.getMessageService() : null;
this.disclaimerService = serviceFactory != null ? serviceFactory.getDisclaimerService() : null;

if (serviceFactory != null && config.isOboConfigured()) {
ExtAppAuthenticator extAppAuthenticator = authenticatorFactory.getExtAppAuthenticator();
this.extAppAuthSession = extAppAuthenticator.authenticateExtApp();
this.extAppServices = new ExtAppServices(apiClientFactory.getPodClient(),
this.extAppAuthSession,
new RetryWithRecoveryBuilder<>().retryConfig(this.config.getRetry()));
} else {
this.extAppServices = null;
this.extAppAuthSession = null;
}

// retrieve bot session info
this.botInfo = sessionService != null ? sessionService.getSession() : null;

Expand Down Expand Up @@ -304,6 +318,10 @@ public OboServices obo(AuthSession oboSession) {
return new OboServices(config, oboSession);
}

public ExtAppServices apps() {
return this.extAppServices;
}

/**
* Returns the {@link ExtensionAppAuthenticator}.
*
Expand All @@ -313,6 +331,17 @@ public ExtensionAppAuthenticator appAuthenticator() {
return this.getExtensionAppAuthenticator();
}

/**
* Returns the extension app auth session.
*
* @return extension app auth session.
*/
@API(status = API.Status.EXPERIMENTAL)
public ExtAppAuthSession extAppAuthSession() {
return Optional.ofNullable(this.extAppAuthSession)
.orElseThrow(() -> new IllegalStateException("Cannot get App auth session. Ext app is not configured."));
}

/**
* Returns the Bot session.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ public interface AuthenticatorFactory {
*/
@Nonnull
ExtensionAppAuthenticator getExtensionAppAuthenticator() throws AuthInitializationException;

/**
* Creates a new instance of a {@link ExtAppAuthenticator}.
*
* @return a new {@link ExtAppAuthenticator} instance.
* @throws AuthInitializationException if the authenticator cannot be instantiated.
*/
@Nonnull
ExtAppAuthenticator getExtAppAuthenticator() throws AuthInitializationException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.symphony.bdk.core.auth;

import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;

import org.apiguardian.api.API;

import javax.annotation.Nullable;

/**
* Extension App Authentication session handle. The {@link ExtAppAuthSession#refresh()} will trigger a re-auth against the API endpoints.
*/
@API(status = API.Status.STABLE)
public interface ExtAppAuthSession {
/**
* Extension app session token.
*
* @return extension app session token
*/
@Nullable
String getAppSession();

/**
* Trigger re-authentication to refresh session token.
*/
void refresh() throws AuthUnauthorizedException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.symphony.bdk.core.auth;

import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
import org.apiguardian.api.API;

import javax.annotation.Nonnull;

/**
* Extension App authenticator service.
*/
@API(status = API.Status.STABLE)
public interface ExtAppAuthenticator {

/**
* Authenticates an extension app.
*
* @return the authentication session.
*/
@Nonnull ExtAppAuthSession authenticateExtApp() throws AuthUnauthorizedException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.symphony.bdk.core.auth.impl;

import com.symphony.bdk.core.auth.ExtAppAuthenticator;
import com.symphony.bdk.core.auth.OboAuthenticator;
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;
import com.symphony.bdk.core.config.model.BdkRetryConfig;
import com.symphony.bdk.http.api.ApiException;

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

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

protected final String appId;
private final AuthenticationRetry<String> authenticationRetry;

protected AbstractExtAppAuthenticator(BdkRetryConfig retryConfig, String appId) {
this.appId = appId;
this.authenticationRetry = new AuthenticationRetry<>(retryConfig);
}

protected String retrieveAppSessionToken() throws AuthUnauthorizedException {
log.debug("Start authenticating app with id : {} ...", appId);

final String unauthorizedErrorMessage = "Unable to authenticate app with ID : " + appId + ". "
+ "It usually happens when the app has not been configured or is not activated.";

return authenticationRetry.executeAndRetry("AbstractExtAppAuthenticator.retrieveAppSessionToken", getBasePath(),
this::authenticateAndRetrieveAppSessionToken, unauthorizedErrorMessage);
}

protected abstract String authenticateAndRetrieveAppSessionToken() throws ApiException;

protected abstract String getBasePath();
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,38 @@ ExtensionAppAuthenticator getExtensionAppAuthenticator() throws AuthInitializati
throw new AuthInitializationException("Neither RSA private key nor certificate is configured.");
}

@Nonnull
@Override
public ExtAppAuthenticator getExtAppAuthenticator() throws AuthInitializationException {
if (this.config.getApp().isBothCertificateAndRsaConfigured()) {
throw new AuthInitializationException(
"Both of certificate and rsa authentication are configured. Only one of them should be provided.");
}
if (this.config.getApp().isCertificateAuthenticationConfigured()) {
if (!this.config.getApp().isCertificateConfigurationValid()) {
throw new AuthInitializationException(
"Only one of certificate path or content should be configured for app authentication.");
}
return new ExtAppAuthenticatorCertImpl(
this.config.getRetry(),
this.config.getApp().getAppId(),
this.apiClientFactory.getExtAppSessionAuthClient());
}
if (this.config.getApp().isRsaAuthenticationConfigured()) {
if (!this.config.getApp().isRsaConfigurationValid()) {
throw new AuthInitializationException(
"Only one of private key path or content should be configured for app authentication.");
}
return new ExtAppAuthenticatorRsaImpl(
this.config.getRetry(),
this.config.getApp().getAppId(),
this.loadPrivateKeyFromAuthenticationConfig(this.config.getApp()),
this.apiClientFactory.getLoginClient()
);
}
throw new AuthInitializationException("Neither RSA private key nor certificate is configured.");
}

private PrivateKey loadPrivateKeyFromAuthenticationConfig(BdkAuthenticationConfig config)
throws AuthInitializationException {
String privateKeyPath = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.symphony.bdk.core.auth.impl;

import com.symphony.bdk.core.auth.ExtAppAuthSession;
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;

import org.apiguardian.api.API;
import org.jetbrains.annotations.Nullable;

/**
* {@link ExtAppAuthSession} impl for Extension App Certificate authentication mode.
*/
@API(status = API.Status.INTERNAL)
public class ExtAppAuthSessionCertImpl implements ExtAppAuthSession {

String appSession;
ExtAppAuthenticatorCertImpl authenticator;

public ExtAppAuthSessionCertImpl(ExtAppAuthenticatorCertImpl authenticator) {
this.authenticator = authenticator;
}

@Nullable
@Override
public String getAppSession() {
return appSession;
}

@Override
public void refresh() throws AuthUnauthorizedException {
this.appSession = this.authenticator.retrieveAppSessionToken();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.symphony.bdk.core.auth.impl;

import com.symphony.bdk.core.auth.ExtAppAuthSession;
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;

import org.apiguardian.api.API;
import org.jetbrains.annotations.Nullable;

/**
* {@link ExtAppAuthSession} impl for Extension App RSA authentication mode.
*/
@API(status = API.Status.INTERNAL)
public class ExtAppAuthSessionImpl implements ExtAppAuthSession {
ExtAppAuthenticatorRsaImpl authenticator;
String appSession;

public ExtAppAuthSessionImpl(ExtAppAuthenticatorRsaImpl authenticator) {
this.authenticator = authenticator;
}

@Nullable
@Override
public String getAppSession() {
return appSession;
}

@Override
public void refresh() throws AuthUnauthorizedException {
this.appSession = this.authenticator.retrieveAppSessionToken();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.symphony.bdk.core.auth.impl;

import com.symphony.bdk.core.auth.ExtAppAuthSession;
import com.symphony.bdk.core.auth.exception.AuthUnauthorizedException;

import com.symphony.bdk.core.config.model.BdkRetryConfig;
import com.symphony.bdk.gen.api.CertificateAuthenticationApi;
import com.symphony.bdk.http.api.ApiClient;
import com.symphony.bdk.http.api.ApiException;

import org.apiguardian.api.API;
import org.jetbrains.annotations.NotNull;

@API(status = API.Status.INTERNAL)
public class ExtAppAuthenticatorCertImpl extends AbstractExtAppAuthenticator {
private final CertificateAuthenticationApi authenticationApi;

public ExtAppAuthenticatorCertImpl(BdkRetryConfig retryConfig,
String appId,
ApiClient loginApiClient) {
super(retryConfig, appId);
this.authenticationApi = new CertificateAuthenticationApi(loginApiClient);
}

@NotNull
@Override
public ExtAppAuthSession authenticateExtApp() throws AuthUnauthorizedException {
ExtAppAuthSession session = new ExtAppAuthSessionCertImpl(this);
session.refresh();
return session;
}

@Override
protected String authenticateAndRetrieveAppSessionToken() throws ApiException {
return this.authenticationApi.v1AppAuthenticatePost().getToken();
}

@Override
protected String getBasePath() {
return authenticationApi.getApiClient().getBasePath();
}
}
Loading
Loading