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

APP-2887 - Authentication API #173

Merged
merged 22 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f2da794
APP-2887 [draft] Authentication API initial commit
thibauult Aug 4, 2020
f3ce243
APP-2887 Add API statuses
thibauult Aug 4, 2020
fde5a81
APP-2887 Refacto, added @Nullable and @Nonnull
thibauult Aug 4, 2020
869cece
APP-2887 Created BotAuthenticator interface
thibauult Aug 5, 2020
ca57cea
APP-2887 Introduced SymphonyBdk facade object, discover ApiClient imp…
thibauult Aug 5, 2020
a6f3379
APP-2887 Improved exception management, introduced ApiClientProvider
thibauult Aug 5, 2020
7ff1290
Merge remote-tracking branch 'upstream/master' into APP-2887_Auth-API…
thibauult Aug 5, 2020
b9b5936
Merge remote-tracking branch 'upstream/master' into APP-2887_Auth-API…
thibauult Aug 5, 2020
094d900
APP-2887 Merged new Config API
thibauult Aug 5, 2020
71c5b4a
APP-2887 More documentation, exception management, added AuthDeepDive…
thibauult Aug 6, 2020
bb78690
APP-2887 Renamed core examples module
thibauult Aug 6, 2020
79cb884
Merge remote-tracking branch 'upstream/master' into APP-2887_Auth-API…
thibauult Aug 6, 2020
d7a9556
APP-2887 Renamed config exception package
thibauult Aug 6, 2020
6d99f22
APP-2887 Applied @symphony-hong review comments
thibauult Aug 7, 2020
4e93fed
APP-2887 Added uni tests
thibauult Aug 7, 2020
f45e5ba
APP-2887 Added uni tests
thibauult Aug 7, 2020
e6d03ac
APP-2887 Added missing doc
thibauult Aug 10, 2020
8c252d3
APP-2887 Simplified SymphonyBdk
thibauult Aug 10, 2020
69e1baf
APP-2887 Fixed review comments from @symphonydarlys and @symphony-youri
thibauult Aug 10, 2020
36f2745
Merge remote-tracking branch 'upstream/master' into APP-2887_Auth-API…
thibauult Aug 10, 2020
bd69a8d
APP-2887 Renamed RsaHelper to RsaTestHelper as asked by @symphonydarlys
thibauult Aug 10, 2020
3dd45d5
APP-2887 Fixed code coverage (90%)
thibauult Aug 10, 2020
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Symphony Java BDK [![CircleCI](https://circleci.com/gh/SymphonyPlatformSolutions/symphony-api-client-java.svg?style=shield)](https://circleci.com/gh/SymphonyPlatformSolutions/symphony-api-client-java) [![Known Vulnerabilities](https://snyk.io/test/github/SymphonyPlatformSolutions/symphony-api-client-java/badge.svg)](https://snyk.io/test/github/SymphonyPlatformSolutions/symphony-api-client-java) [![License: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](https://opensource.org/licenses/MIT) [![Email](https://img.shields.io/static/v1?label=contact&message=email&color=darkgoldenrod)](mailto:platformsolutions@symphony.com?subject=Java%20SDK)
# Symphony Java BDK
[![CircleCI](https://circleci.com/gh/SymphonyPlatformSolutions/symphony-api-client-java.svg?style=shield)](https://circleci.com/gh/SymphonyPlatformSolutions/symphony-api-client-java)
[![Known Vulnerabilities](https://snyk.io/test/github/SymphonyPlatformSolutions/symphony-api-client-java/badge.svg)](https://snyk.io/test/github/SymphonyPlatformSolutions/symphony-api-client-java)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.symphony.platformsolutions/symphony-api-client-java/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.symphony.platformsolutions/symphony-api-client-java)
[![License: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](https://opensource.org/licenses/MIT)
[![Email](https://img.shields.io/static/v1?label=contact&message=email&color=darkgoldenrod)](mailto:platformsolutions@symphony.com?subject=Java%20SDK)

The Symphony Java BDK helps you to create Bots and Applications on top of the [Symphony REST APIs](https://developers.symphony.com/restapi/reference).

Expand Down
3 changes: 3 additions & 0 deletions docs/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Authentication

To be done...
Copy link
Contributor

@symphonydarlys symphonydarlys Aug 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create a new story to create the documentation!!! please mention the JIRA story here and put in the ticket as acceptance criteria : "Documentation must contain explanation about Client Provider and what happens if 2 different providers are configured"

7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<lombok.version>1.18.12</lombok.version>
<jacoco-maven-plugin.version>0.8.5</jacoco-maven-plugin.version>
<migbase64.version>2.2</migbase64.version>
<resilience4j-retry.version>1.4.0</resilience4j-retry.version>
<slf4j.version>1.7.30</slf4j.version>

<!-- Test dependencies -->
Expand Down Expand Up @@ -105,6 +106,12 @@
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-retry</artifactId>
<version>${resilience4j-retry.version}</version>
</dependency>

<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.symphony.bdk.core.api.invoker;

/**
* New {@link ApiClient} instances provider.
*/
public interface ApiClientProvider {

/**
* Creates a new {@link ApiClient} instance.
*
* @return a new {@link ApiClient} instance.
*/
ApiClient newInstance();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really good approach, we can use different libraries to generate clients!!!

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,13 @@ public ApiException(int code, String message, Map<String, List<String>> response
this.responseHeaders = responseHeaders;
this.responseBody = responseBody;
}

/**
* Check if response status if unauthorized or not.
*
* @return true if response status is 401, false otherwise
*/
public boolean isUnauthorized() {
return this.code == 401;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use Enum HttpStatus instead of

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.symphony.bdk.core.api.invoker;

import lombok.Getter;
import org.apiguardian.api.API;

import java.util.List;
import java.util.Map;

/**
* Runtime version of the {@link ApiException}.
*/
@Getter
@API(status = API.Status.EXPERIMENTAL)
public class ApiRuntimeException extends RuntimeException {

private final int code;
private final Map<String, List<String>> responseHeaders;
private final String responseBody;

public ApiRuntimeException(ApiException source) {
super(source);
this.code = source.getCode();
this.responseHeaders = source.getResponseHeaders();
this.responseBody = source.getResponseBody();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.symphony.bdk.core.api.invoker.jersey2;

import com.symphony.bdk.core.api.invoker.ApiClient;
import com.symphony.bdk.core.api.invoker.ApiClientProvider;

/**
* Provides new {@link ApiClientJersey2} implementation of the {@link ApiClient} interface.
*/
public class ApiClientProviderJersey2 implements ApiClientProvider {

/**
* {@inheritDoc}
*/
@Override
public ApiClient newInstance() {
return new ApiClientJersey2();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.symphony.bdk.core.api.invoker.jersey2.ApiClientProviderJersey2
17 changes: 17 additions & 0 deletions symphony-bdk-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.11.0</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

management of version in properties sections is better!!!

</dependency>

<!-- ******************************** -->
<!-- * CodeGen related dependencies * -->
<!-- ******************************** -->
Expand Down Expand Up @@ -129,6 +135,17 @@
<artifactId>mockserver-netty</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.symphony.platformsolutions</groupId>
<artifactId>symphony-bdk-core-invoker-jersey2</artifactId>
<version>1.2.0-SNAPSHOT</version>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem!!!

<scope>test</scope>
</dependency>

</dependencies>

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.symphony.bdk.core;

import com.symphony.bdk.core.auth.AuthSession;
import com.symphony.bdk.core.auth.AuthenticatorFactory;
import com.symphony.bdk.core.auth.OboAuthenticator;
import com.symphony.bdk.core.auth.exception.AuthInitializationException;
import com.symphony.bdk.core.service.Obo;
import com.symphony.bdk.core.client.ApiClientFactory;
import com.symphony.bdk.core.config.model.BdkConfig;
import com.symphony.bdk.core.service.V4MessageService;

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

/**
* BDK entry point.
*/
@Slf4j
@API(status = API.Status.EXPERIMENTAL)
public class SymphonyBdk {

private final ApiClientFactory apiClientFactory;

private final AuthSession botSession;
private final OboAuthenticator oboAuthenticator;

public SymphonyBdk(BdkConfig config) throws AuthInitializationException {

this.apiClientFactory = new ApiClientFactory(config);

final AuthenticatorFactory authenticatorFactory = new AuthenticatorFactory(
config,
apiClientFactory.getLoginClient(),
apiClientFactory.getRelayClient()
);

this.botSession = authenticatorFactory.getBotAuthenticator().authenticateBot();
this.oboAuthenticator = authenticatorFactory.getOboAuthenticator();
}

public V4MessageService messages() {
return new V4MessageService(this.apiClientFactory.getAgentClient(), this.botSession);
}

public V4MessageService messages(Obo.Handle oboHandle) {
AuthSession oboSession;
if (oboHandle.hasUsername()) {
oboSession = this.oboAuthenticator.authenticateByUsername(oboHandle.getUsername());
} else {
oboSession = this.oboAuthenticator.authenticateByUserId(oboHandle.getUserId());
}
return new V4MessageService(this.apiClientFactory.getAgentClient(), oboSession);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.symphony.bdk.core.auth;

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

import org.apiguardian.api.API;

import javax.annotation.Nullable;

/**
* Authentication session handle. The {@link AuthSession#refresh()} will trigger a re-auth against the API endpoints.
* <p>
* You should keep using the same token until you receive a HTTP 401, at which you should re-authenticate and
* get a new token for a new session.
* </p>
*/
@API(status = API.Status.STABLE)
public interface AuthSession {

/**
* Pod's authentication token.
*
* @return the Pod session token
*/
@Nullable String getSessionToken();

/**
* KeyManager's authentication token.
*
* @return the KeyManager token, null if OBO
*/
@Nullable String getKeyManagerToken();

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

import com.symphony.bdk.core.api.invoker.ApiClient;
import com.symphony.bdk.core.auth.exception.AuthInitializationException;
import com.symphony.bdk.core.auth.impl.BotAuthenticatorRSAImpl;
import com.symphony.bdk.core.auth.impl.OboAuthenticatorRSAImpl;
import com.symphony.bdk.core.auth.jwt.JwtHelper;
import com.symphony.bdk.core.config.model.BdkConfig;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apiguardian.api.API;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

import javax.annotation.Nonnull;

/**
* Factory class that provides new instances for the main authenticators :
* <ul>
* <li>{@link BotAuthenticator} : to authenticate the main Bot service account</li>
* <li>{@link OboAuthenticator} : to perform on-behalf-of authentication</li>
* </ul>
*/
@Slf4j
@API(status = API.Status.STABLE)
public class AuthenticatorFactory {

private final BdkConfig config;
private final ApiClient loginApiClient;
private final ApiClient relayApiClient;

private JwtHelper jwtHelper = new JwtHelper();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be final?


public AuthenticatorFactory(@Nonnull BdkConfig bdkConfig, @Nonnull ApiClient loginClient, @Nonnull ApiClient relayClient) {
this.config = bdkConfig;
this.loginApiClient = loginClient;
this.relayApiClient = relayClient;
}

/**
* Creates a new instance of a {@link BotAuthenticator} service.
*
* @return a new {@link BotAuthenticator} instance.
*/
public @Nonnull BotAuthenticator getBotAuthenticator() throws AuthInitializationException {

return new BotAuthenticatorRSAImpl(
this.config.getBot().getUsername(),
this.loadPrivateKeyFromPath(this.config.getBot().getPrivateKeyPath()),
this.loginApiClient,
this.relayApiClient
);
}

/**
* Creates a new instance of an {@link OboAuthenticator} service.
*
* @return a new {@link OboAuthenticator} instance.
*/
public @Nonnull OboAuthenticator getOboAuthenticator() throws AuthInitializationException {

return new OboAuthenticatorRSAImpl(
this.config.getApp().getAppId(),
this.loadPrivateKeyFromPath(this.config.getApp().getPrivateKeyPath()),
this.loginApiClient
);
}

private PrivateKey loadPrivateKeyFromPath(String privateKeyPath) throws AuthInitializationException {
log.debug("Loading RSA privateKey from path : {}", privateKeyPath);
try {
return this.jwtHelper.parseRSAPrivateKey(IOUtils.toString(new FileInputStream(privateKeyPath), StandardCharsets.UTF_8));
} catch (GeneralSecurityException e) {
final String message = "Unable to parse RSA Private Key located at " + privateKeyPath;
log.error(message, e);
throw new AuthInitializationException(message, e);
} catch (IOException e) {
final String message = "Unable to read or find RSA Private Key from path " + privateKeyPath;
log.error(message, e);
throw new AuthInitializationException(message, e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.symphony.bdk.core.auth;

import org.apiguardian.api.API;

import javax.annotation.Nonnull;

/**
* Bot authenticator service.
*/
@API(status = API.Status.STABLE)
public interface BotAuthenticator {

/**
* Authenticates a Bot's service account.
*
* @return the authentication session.
*/
@Nonnull AuthSession authenticateBot();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.symphony.bdk.core.auth;

import org.apiguardian.api.API;

import javax.annotation.Nonnull;

/**
* On-behalf-of authenticator service.
*/
@API(status = API.Status.STABLE)
public interface OboAuthenticator {

/**
* Authenticates on-behalf-of a particular user using his username.
*
* @param username Username of the user.
* @return the authentication session.
*/
@Nonnull AuthSession authenticateByUsername(@Nonnull String username);

/**
* Authenticates on behalf of a particular user using his userId.
*
* @param userId Id of the user.
* @return the authentication sessions.
*/
@Nonnull AuthSession authenticateByUserId(@Nonnull Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.symphony.bdk.core.auth.exception;

import org.apiguardian.api.API;

import javax.annotation.Nonnull;

/**
* Thrown when unable to read/parse a RSA Private Key or a certificate.
*/
@API(status = API.Status.STABLE)
public class AuthInitializationException extends Exception {

public AuthInitializationException(@Nonnull String message, @Nonnull Throwable source) {
super(message, source);
}
}
Loading