Fixed#5428: added support for auth0 sso (#5429)

This commit is contained in:
Parth Panchal 2022-06-14 13:48:45 +05:30 committed by GitHub
parent d65c97c4e8
commit 9357c131a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 165 additions and 54 deletions

View File

@ -57,7 +57,7 @@
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>

View File

@ -0,0 +1,29 @@
package org.openmetadata.client.interceptors;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.openmetadata.catalog.security.client.Auth0SSOClientConfig;
import org.openmetadata.client.model.AccessTokenResponse;
public class Auth0AccessTokenRequestInterceptor implements RequestInterceptor {
private final Auth0SSOClientConfig securityConfig;
public Auth0AccessTokenRequestInterceptor(Auth0SSOClientConfig config) {
securityConfig = config;
}
@Override
public void apply(RequestTemplate requestTemplate) {
String requestBody =
"grant_type="+ AccessTokenResponse.GrantType.CLIENT_CREDENTIALS
+ "&client_id="
+ securityConfig.getClientId()
+ "&client_secret="
+ securityConfig.getSecretKey()
+ "&audience=https://"
+ securityConfig.getDomain()
+ "/api/v2/";
requestTemplate.body(requestBody);
}
}

View File

@ -16,7 +16,10 @@ package org.openmetadata.client.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
public class OktaAccessTokenResponse {
import java.util.HashMap;
import java.util.Map;
public class AccessTokenResponse {
@JsonProperty("token_type")
private String tokenType = null;
@ -43,7 +46,7 @@ public class OktaAccessTokenResponse {
this.tokenType = description;
}
public OktaAccessTokenResponse withTokenType(String displayName) {
public AccessTokenResponse withTokenType(String displayName) {
this.tokenType = displayName;
return this;
}
@ -62,7 +65,7 @@ public class OktaAccessTokenResponse {
this.expiresIn = iExpiresIn;
}
public OktaAccessTokenResponse withExpiresIn(Long iExpiresIn) {
public AccessTokenResponse withExpiresIn(Long iExpiresIn) {
this.expiresIn = iExpiresIn;
return this;
}
@ -81,7 +84,7 @@ public class OktaAccessTokenResponse {
this.accessToken = iAccessToken;
}
public OktaAccessTokenResponse withAccessToken(String iAccessToken) {
public AccessTokenResponse withAccessToken(String iAccessToken) {
this.accessToken = iAccessToken;
return this;
}
@ -100,8 +103,45 @@ public class OktaAccessTokenResponse {
this.scope = iScopes;
}
public OktaAccessTokenResponse withScope(String iScopes) {
public AccessTokenResponse withScope(String iScopes) {
this.scope = iScopes;
return this;
}
public enum GrantType {
AUTHORIZATION_CODE("authorization_code"),
CLIENT_CREDENTIALS("client_credentials"),
IMPLICIT("implicit");
private final String value;
private static final Map<String, AccessTokenResponse.GrantType> CONSTANTS =
new HashMap<>();
static {
for (AccessTokenResponse.GrantType c : values()) {
CONSTANTS.put(c.value, c);
}
}
GrantType(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
public String value() {
return this.value;
}
public static AccessTokenResponse.GrantType fromValue(String value) {
AccessTokenResponse.GrantType constant = CONSTANTS.get(value);
if (constant == null) {
throw new IllegalArgumentException(value);
} else {
return constant;
}
}
}
}

View File

@ -14,9 +14,7 @@
package org.openmetadata.client.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OktaSSOConfig {
/** Okta Client ID for the service application. (Required) */
@ -120,40 +118,4 @@ public class OktaSSOConfig {
return sb.toString();
}
public enum GrantType {
AUTHORIZATION_CODE("authorization_code"),
CLIENT_CREDENTIALS("client_credentials"),
IMPLICIT("implicit");
private final String value;
private static final Map<String, OktaSSOConfig.GrantType> CONSTANTS =
new HashMap<String, OktaSSOConfig.GrantType>();
static {
for (OktaSSOConfig.GrantType c : values()) {
CONSTANTS.put(c.value, c);
}
}
GrantType(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
public String value() {
return this.value;
}
public static OktaSSOConfig.GrantType fromValue(String value) {
OktaSSOConfig.GrantType constant = CONSTANTS.get(value);
if (constant == null) {
throw new IllegalArgumentException(value);
} else {
return constant;
}
}
}
}

View File

@ -14,25 +14,87 @@
package org.openmetadata.client.security;
import feign.RequestTemplate;
import io.swagger.client.ApiClient;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.openmetadata.catalog.security.client.Auth0SSOClientConfig;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.interceptors.Auth0AccessTokenRequestInterceptor;
import org.openmetadata.client.model.AccessTokenResponse;
import org.openmetadata.client.security.interfaces.Auth0AccessTokenApi;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Auth0AuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(Auth0AuthenticationProvider.class);
private OpenMetadataServerConnection serverConfig;
private final Auth0SSOClientConfig securityConfig;
private String generatedAuthToken;
private Long expirationTimeMillis;
private final Auth0AccessTokenApi auth0SSOClient;
public Auth0AuthenticationProvider(OpenMetadataServerConnection iConfig) {
if (!iConfig.getAuthProvider().equals(OpenMetadataServerConnection.AuthProvider.AUTH_0)) {
LOG.error("Required type to invoke is Auth0 for Auth0Authentication Provider");
throw new RuntimeException("Required type to invoke is Auth0 for Auth0Authentication Provider");
}
serverConfig = iConfig;
securityConfig = (Auth0SSOClientConfig) iConfig.getSecurityConfig();
if (securityConfig == null) {
LOG.error("Security Config is missing, it is required");
throw new RuntimeException("Security Config is missing, it is required");
}
generatedAuthToken = "";
ApiClient auth0SSO = new ApiClient();
auth0SSO.setBasePath("https://" + securityConfig.getDomain());
Auth0AccessTokenRequestInterceptor interceptor = new Auth0AccessTokenRequestInterceptor(securityConfig);
auth0SSO.addAuthorization("0AuthToken", interceptor);
auth0SSOClient = auth0SSO.buildClient(Auth0AccessTokenApi.class);
}
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return null;
return new Auth0AuthenticationProvider(iConfig);
}
@Override
public String authToken() {
return null;
AccessTokenResponse resp = auth0SSOClient.getAccessToken();
generatedAuthToken = resp.getAccessToken();
expirationTimeMillis = Date.from(Instant.now().plus(resp.getExpiresIn(), ChronoUnit.SECONDS)).getTime();
return generatedAuthToken;
}
@Override
public String getAccessToken() {
return null;
return generatedAuthToken;
}
@Override
public void apply(RequestTemplate requestTemplate) {}
public void apply(RequestTemplate requestTemplate) {
if (requestTemplate.url().contains("version")) {
return;
}
if (requestTemplate.headers().containsKey("Authorization")) {
return;
}
// If first time, get the token
if (expirationTimeMillis == null || System.currentTimeMillis() >= expirationTimeMillis) {
this.authToken();
}
if (getAccessToken() != null) {
requestTemplate.header("Authorization", "Bearer " + getAccessToken());
}
}
}

View File

@ -20,7 +20,7 @@ import java.time.temporal.ChronoUnit;
import java.util.Date;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.interceptors.OktaAccessTokenRequestInterceptor;
import org.openmetadata.client.model.OktaAccessTokenResponse;
import org.openmetadata.client.model.AccessTokenResponse;
import org.openmetadata.client.model.OktaSSOConfig;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
import org.openmetadata.client.security.interfaces.OktaAccessTokenApi;
@ -67,7 +67,7 @@ public class OktaAuthenticationProvider implements AuthenticationProvider {
@Override
public String authToken() {
OktaAccessTokenResponse resp = oktaSSOClient.getAccessToken("client_credentials", "test");
AccessTokenResponse resp = oktaSSOClient.getAccessToken("client_credentials", "test");
generatedAuthToken = resp.getAccessToken();
expirationTimeMillis = Date.from(Instant.now().plus(resp.getExpiresIn(), ChronoUnit.SECONDS)).getTime();
return generatedAuthToken;

View File

@ -14,6 +14,7 @@
package org.openmetadata.client.security.factory;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.Auth0AuthenticationProvider;
import org.openmetadata.client.security.AzureAuthenticationProvider;
import org.openmetadata.client.security.GoogleAuthenticationProvider;
import org.openmetadata.client.security.NoOpAuthenticationProvider;
@ -30,6 +31,7 @@ public class AuthenticationProviderFactory {
case OKTA:
return new OktaAuthenticationProvider(serverConfig);
case AUTH_0:
return new Auth0AuthenticationProvider(serverConfig);
case CUSTOM_OIDC:
case AZURE:
return new AzureAuthenticationProvider(serverConfig);

View File

@ -0,0 +1,16 @@
package org.openmetadata.client.security.interfaces;
import feign.Headers;
import feign.RequestLine;
import io.swagger.client.ApiClient;
import org.openmetadata.client.model.AccessTokenResponse;
public interface Auth0AccessTokenApi extends ApiClient.Api {
@RequestLine("POST /oauth/token")
@Headers({
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
AccessTokenResponse getAccessToken();
}

View File

@ -16,7 +16,7 @@ package org.openmetadata.client.security.interfaces;
import feign.*;
import io.swagger.client.ApiClient;
import java.util.Map;
import org.openmetadata.client.model.OktaAccessTokenResponse;
import org.openmetadata.client.model.AccessTokenResponse;
public interface OktaAccessTokenApi extends ApiClient.Api {
@RequestLine(
@ -25,7 +25,7 @@ public interface OktaAccessTokenApi extends ApiClient.Api {
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
OktaAccessTokenResponse getAccessToken(
AccessTokenResponse getAccessToken(
@Param("grant_type") String grant_type,
@Param("scope") String scope,
@Param("client_assertion_type") String client_assertion_type,
@ -37,12 +37,12 @@ public interface OktaAccessTokenApi extends ApiClient.Api {
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
OktaAccessTokenResponse getAccessToken(@QueryMap(encoded = true) Map<String, Object> queryParams);
AccessTokenResponse getAccessToken(@QueryMap(encoded = true) Map<String, Object> queryParams);
@RequestLine("POST /v1/token?grant_type={grant_type}&scope={scope}")
@Headers({
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
OktaAccessTokenResponse getAccessToken(@Param("grant_type") String grant_type, @Param("scope") String scope);
AccessTokenResponse getAccessToken(@Param("grant_type") String grant_type, @Param("scope") String scope);
}