[Backend] Java Client SDK [WIP] (#5149)

* [Backend] Java Client SDK #4961 [WIP]

* [Backend] Java Client SDK #4961 ..Fix the OpenMetadata[lowercase d] added version plugin

* [Backend] Java Client SDK #4961 ..Add Version utils to core and Using Custom Interceptor to modify request body as per OMD supported JSON Schema

* [Backend] Using exclude nulls to modify request

* [Backend] Google SSO changes tested and fixed for Service Credentials

* [Backend] Okta SSO added
This commit is contained in:
mohitdeuex 2022-06-06 11:22:43 +05:30 committed by GitHub
parent ec1aca926a
commit a299cfbc86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1181 additions and 1 deletions

View File

@ -95,7 +95,7 @@ public class JwtFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
if (uriInfo.getPath().contains("config")) {
if (uriInfo.getPath().contains("config") || uriInfo.getPath().contains("version")) {
return;
}

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>openmetadata-clients</artifactId>
<groupId>org.openmetadata</groupId>
<version>0.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>openmetadata-java-client</artifactId>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<springfox-version>2.7.0</springfox-version>
<feign-version>9.7.0</feign-version>
</properties>
<dependencies>
<dependency>
<groupId>org.openmetadata</groupId>
<artifactId>openmetadata-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.0-rc1</version>
</dependency>
<dependency>
<groupId>com.github.joschi.jackson</groupId>
<artifactId>jackson-datatype-threetenbp</artifactId>
<version>2.6.4</version>
</dependency>
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.client</artifactId>
<version>1.0.2</version>
</dependency>
<!-- FEIGN DEPENDENCY-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>${feign-version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>${feign-version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>${feign-version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>1.3.0</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--Reformat Code to comply with Google Java Style -->
<plugin>
<groupId>com.theoryinpractise</groupId>
<artifactId>googleformatter-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-src-plugin.version}</version>
<configuration>
<skipSource>true</skipSource>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
<configuration>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
<outputDirectory>${project.build.directory}/classes/catalog</outputDirectory>
<outputName>VERSION</outputName>
</configuration>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>create-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.16</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/../../catalog-rest-service/target/classes/assets/swagger.yaml</inputSpec>
<language>java</language>
<library>feign</library>
<generateApiTests>true</generateApiTests>
<generateModelTests>true</generateModelTests>
<configOptions>
<generateForOpenFeign>true</generateForOpenFeign>
<sourceFolder>src/main/java/</sourceFolder>
</configOptions>
<output>${project.build.directory}/generated-sources/swagger
</output>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,100 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.gateway;
import com.fasterxml.jackson.annotation.JsonInclude;
import feign.RequestInterceptor;
import io.swagger.client.ApiClient;
import io.swagger.client.api.CatalogApi;
import org.openmetadata.catalog.api.CatalogVersion;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.interceptors.CustomRequestInterceptor;
import org.openmetadata.client.security.factory.AuthenticationProviderFactory;
import org.openmetadata.core.util.VersionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OpenMetadata {
private static final Logger LOG = LoggerFactory.getLogger(OpenMetadata.class);
private static final CatalogVersion CATALOG_VERSION_CLIENT;
static {
CATALOG_VERSION_CLIENT = VersionUtils.getCatalogVersion("/catalog/VERSION");
}
private ApiClient apiClient;
private OpenMetadataServerConnection serverConfig;
private String basePath;
private final String requestInterceptorKey = "custom";
public OpenMetadata(OpenMetadataServerConnection config) {
serverConfig = config;
apiClient = new ApiClient();
AuthenticationProviderFactory factory = new AuthenticationProviderFactory();
apiClient.addAuthorization("oauth", factory.getAuthProvider(config));
basePath = config.getHostPort() + "/";
apiClient.setBasePath(basePath);
apiClient.getObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
validateVersion();
}
public <T extends ApiClient.Api> T buildClient(Class<T> clientClass) {
return apiClient.buildClient(clientClass);
}
public <T extends ApiClient.Api, K> T buildClient(Class<T> clientClass, Class<K> requestClass) {
updateRequestType(requestClass);
return apiClient.buildClient(clientClass);
}
public <K> void updateRequestType(Class<K> requestClass) {
if (apiClient.getApiAuthorizations().containsKey(requestInterceptorKey)) {
apiClient.getApiAuthorizations().remove(requestInterceptorKey);
}
CustomRequestInterceptor<K> newInterceptor =
new CustomRequestInterceptor(apiClient.getObjectMapper(), requestClass);
apiClient.addAuthorization(requestInterceptorKey, newInterceptor);
return;
}
public void addRequestInterceptor(String requestInterceptorKey, RequestInterceptor interceptor) {
if (apiClient.getApiAuthorizations().containsKey(requestInterceptorKey)) {
LOG.info("Interceptor with this key already exists");
return;
}
apiClient.addAuthorization(requestInterceptorKey, interceptor);
return;
}
public void validateVersion() {
String clientVersion = getClientVersion();
String serverVersion = getServerVersion();
if (serverVersion.equals(clientVersion)) {
LOG.debug("OpenMetaData Client Initialized successfully.");
} else {
LOG.error(
"OpenMetaData Client Failed to be Initialized successfully. Version mismatch between CLient and Server issue");
}
}
public String getServerVersion() {
CatalogApi api = apiClient.buildClient(CatalogApi.class);
io.swagger.client.model.CatalogVersion serverVersion = api.getCatalogVersion();
return VersionUtils.getVersionFromString(serverVersion.getVersion());
}
public String getClientVersion() {
return VersionUtils.getVersionFromString(CATALOG_VERSION_CLIENT.getVersion());
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.interceptors;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.openmetadata.client.gateway.OpenMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CustomRequestInterceptor<K> implements RequestInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(OpenMetadata.class);
private final Class<K> type;
ObjectMapper mapper;
public CustomRequestInterceptor(ObjectMapper iMapper, Class<K> type) {
this.type = type;
mapper = iMapper;
}
@Override
public void apply(RequestTemplate requestTemplate) {
try {
LOG.debug("Trying to Convert from generated class to org.openmetadata");
String body = new String(requestTemplate.body());
K value = mapper.readValue(body, this.getType());
requestTemplate.body(mapper.writeValueAsString(value));
} catch (Exception ex) {
LOG.error("[CustomInterceptor] Failed in transforming request with exception :" + ex.getMessage());
}
}
public Class<K> getType() {
return this.type;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.interceptors;
import feign.RequestTemplate;
import java.util.Base64;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.model.OktaSSOConfig;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class OktaAccessTokenRequestInterceptor implements AuthenticationProvider {
private OktaSSOConfig securityConfig;
private String base64Credentials;
public OktaAccessTokenRequestInterceptor(OktaSSOConfig config) {
securityConfig = config;
}
@Override
public void apply(RequestTemplate requestTemplate) {
if (requestTemplate.headers().containsKey("Authorization")) {
return;
}
if (base64Credentials == null) {
this.authToken();
}
requestTemplate.header("Authorization", "Basic " + getAccessToken());
}
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return new OktaAccessTokenRequestInterceptor((OktaSSOConfig) iConfig.getSecurityConfig());
}
@Override
public String authToken() {
base64Credentials =
Base64.getEncoder()
.encodeToString((securityConfig.getClientId() + ":" + securityConfig.getClientSecret()).getBytes());
return base64Credentials;
}
@Override
public String getAccessToken() {
return base64Credentials;
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
public class OktaAccessTokenResponse {
@JsonProperty("token_type")
private String tokenType = null;
@JsonProperty("expires_in")
private Long expiresIn = null;
@JsonProperty("access_token")
private String accessToken = null;
@JsonProperty("scope")
private String scope = null;
/**
* Get TokenType
*
* @return TokenType
*/
@Schema(description = "")
public String getTokenType() {
return tokenType;
}
public void setTokenType(String description) {
this.tokenType = description;
}
public OktaAccessTokenResponse withTokenType(String displayName) {
this.tokenType = displayName;
return this;
}
/**
* Get ExpiresIn
*
* @return ExpiresIn
*/
@Schema(description = "")
public Long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Long iExpiresIn) {
this.expiresIn = iExpiresIn;
}
public OktaAccessTokenResponse withExpiresIn(Long iExpiresIn) {
this.expiresIn = iExpiresIn;
return this;
}
/**
* Get AccessToken
*
* @return AccessToken
*/
@Schema(description = "")
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String iAccessToken) {
this.accessToken = iAccessToken;
}
public OktaAccessTokenResponse withAccessToken(String iAccessToken) {
this.accessToken = iAccessToken;
return this;
}
/**
* Get Scope
*
* @return Scope
*/
@Schema(description = "")
public String getScope() {
return scope;
}
public void setScope(String iScopes) {
this.scope = iScopes;
}
public OktaAccessTokenResponse withScope(String iScopes) {
this.scope = iScopes;
return this;
}
}

View File

@ -0,0 +1,159 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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) */
private String clientId;
/** Okta Client Secret for the API service application. (Required) */
private String clientSecret;
/** Okta Authorization Server Url. (Required) */
private String authorizationServerURL;
/** Okta client scopes. */
private List<String> scopes = new ArrayList<String>();
/** Okta Client ID. (Required) */
public String getClientId() {
return clientId;
}
/** Okta Client ID. (Required) */
public void setClientId(String clientId) {
this.clientId = clientId;
}
public OktaSSOConfig withClientId(String clientId) {
this.clientId = clientId;
return this;
}
/** Okta Client Secret. (Required) */
public String getClientSecret() {
return clientSecret;
}
/** Okta Client Secret. (Required) */
public void setClientSecret(String clientId) {
this.clientSecret = clientId;
}
public OktaSSOConfig withClientSecret(String clientId) {
this.clientSecret = clientId;
return this;
}
/** Okta org url. (Required) */
public String getAuthorizationServerURL() {
return authorizationServerURL;
}
/** Okta org url. (Required) */
public void setAuthorizationServerURL(String orgURL) {
this.authorizationServerURL = orgURL;
}
public OktaSSOConfig withAuthorizationServerURL(String orgURL) {
this.authorizationServerURL = orgURL;
return this;
}
/** Okta client scopes. */
public List<String> getScopes() {
return scopes;
}
/** Okta client scopes. */
public void setScopes(List<String> scopes) {
this.scopes = scopes;
}
public OktaSSOConfig withScopes(List<String> scopes) {
this.scopes = scopes;
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(OktaSSOConfig.class.getName())
.append('@')
.append(Integer.toHexString(System.identityHashCode(this)))
.append('[');
sb.append("clientId");
sb.append('=');
sb.append(((this.clientId == null) ? "<null>" : this.clientId));
sb.append("clientSecret");
sb.append('=');
sb.append(((this.clientSecret == null) ? "<null>" : this.clientSecret));
sb.append(',');
sb.append("authorizationServerURL");
sb.append('=');
sb.append(((this.authorizationServerURL == null) ? "<null>" : this.authorizationServerURL));
sb.append(',');
sb.append(',');
sb.append("scopes");
sb.append('=');
sb.append(((this.scopes == null) ? "<null>" : this.scopes));
sb.append(',');
if (sb.charAt((sb.length() - 1)) == ',') {
sb.setCharAt((sb.length() - 1), ']');
} else {
sb.append(']');
}
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

@ -0,0 +1,38 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import feign.RequestTemplate;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class Auth0AuthenticationProvider implements AuthenticationProvider {
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return null;
}
@Override
public String authToken() {
return null;
}
@Override
public String getAccessToken() {
return null;
}
@Override
public void apply(RequestTemplate requestTemplate) {}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import feign.RequestTemplate;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class AzureAuthenticationProvider implements AuthenticationProvider {
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return null;
}
@Override
public String authToken() {
return null;
}
@Override
public String getAccessToken() {
return null;
}
@Override
public void apply(RequestTemplate requestTemplate) {}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import feign.RequestTemplate;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class CustomOIDCAuthenticationProvider implements AuthenticationProvider {
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return null;
}
@Override
public String authToken() {
return null;
}
@Override
public String getAccessToken() {
return null;
}
@Override
public void apply(RequestTemplate requestTemplate) {}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import feign.RequestTemplate;
import java.io.FileInputStream;
import java.util.Arrays;
import org.openmetadata.catalog.security.client.GoogleSSOClientConfig;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GoogleAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(GoogleAuthenticationProvider.class);
private OpenMetadataServerConnection serverConfig;
private GoogleSSOClientConfig securityConfig;
private String generatedAuthToken;
private Long expirationTimeMillis;
private final String OPENID_SCOPE = "https://www.googleapis.com/auth/plus.me";
private final String PROFILE_SCOPE = "https://www.googleapis.com/auth/userinfo.profile";
private final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";
public GoogleAuthenticationProvider(OpenMetadataServerConnection iConfig) {
if (!iConfig.getAuthProvider().equals(OpenMetadataServerConnection.AuthProvider.GOOGLE)) {
LOG.error("Required type to invoke is Google for GoogleAuthentication Provider");
throw new RuntimeException("Required type to invoke is Google for GoogleAuthentication Provider");
}
serverConfig = iConfig;
securityConfig = (GoogleSSOClientConfig) 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 = "";
}
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return new GoogleAuthenticationProvider(iConfig);
}
@Override
public String authToken() {
try {
String credPath = securityConfig.getSecretKey();
String targetAudience = securityConfig.getAudience();
if ((credPath != null && credPath != "") && (targetAudience != null && targetAudience != "")) {
ServiceAccountCredentials saCreds = ServiceAccountCredentials.fromStream(new FileInputStream(credPath));
saCreds =
(ServiceAccountCredentials) saCreds.createScoped(Arrays.asList(OPENID_SCOPE, PROFILE_SCOPE, EMAIL_SCOPE));
IdTokenCredentials tokenCredential =
IdTokenCredentials.newBuilder().setIdTokenProvider(saCreds).setTargetAudience(targetAudience).build();
AccessToken token = tokenCredential.refreshAccessToken();
this.expirationTimeMillis = token.getExpirationTime().getTime();
this.generatedAuthToken = token.getTokenValue();
} else {
LOG.error("Credentials Path or Target Audience is null");
}
} catch (Exception ex) {
LOG.error("Google Authentication Provider error in getting access token" + ex.getMessage());
}
return generatedAuthToken;
}
@Override
public String getAccessToken() {
return generatedAuthToken;
}
@Override
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

@ -0,0 +1,43 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import feign.RequestTemplate;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class NoOpAuthenticationProvider implements AuthenticationProvider {
public NoOpAuthenticationProvider() {}
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return new NoOpAuthenticationProvider();
}
@Override
public String authToken() {
return null;
}
@Override
public String getAccessToken() {
return "";
}
@Override
public void apply(RequestTemplate requestTemplate) {
// no-auth we dont apply anything
return;
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.interceptors.OktaAccessTokenRequestInterceptor;
import org.openmetadata.client.model.OktaAccessTokenResponse;
import org.openmetadata.client.model.OktaSSOConfig;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
import org.openmetadata.client.security.interfaces.OktaAccessTokenApi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OktaAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(GoogleAuthenticationProvider.class);
public static final String clientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
private OpenMetadataServerConnection serverConfig;
private OktaSSOConfig securityConfig;
private String generatedAuthToken;
private Long expirationTimeMillis;
private OktaAccessTokenApi oktaSSOClient;
public OktaAuthenticationProvider(OpenMetadataServerConnection iConfig) {
if (!iConfig.getAuthProvider().equals(OpenMetadataServerConnection.AuthProvider.OKTA)) {
LOG.error("Required type to invoke is OKTA for OKTA Authentication Provider");
throw new RuntimeException("Required type to invoke is OKTA for OKTA Authentication Provider");
}
serverConfig = iConfig;
securityConfig = (OktaSSOConfig) 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 = "";
// Setup Access Token Setting
ApiClient oktaSSO = new ApiClient();
oktaSSO.setBasePath(securityConfig.getAuthorizationServerURL());
OktaAccessTokenRequestInterceptor interceptor = new OktaAccessTokenRequestInterceptor(securityConfig);
oktaSSO.addAuthorization("OAuthToken", interceptor);
oktaSSOClient = oktaSSO.buildClient(OktaAccessTokenApi.class);
}
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return new OktaAuthenticationProvider(iConfig);
}
@Override
public String authToken() {
OktaAccessTokenResponse resp = oktaSSOClient.getAccessToken("client_credentials", "test");
generatedAuthToken = resp.getAccessToken();
expirationTimeMillis = Date.from(Instant.now().plus(resp.getExpiresIn(), ChronoUnit.SECONDS)).getTime();
return generatedAuthToken;
}
@Override
public String getAccessToken() {
return generatedAuthToken;
}
@Override
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

@ -0,0 +1,38 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security;
import feign.RequestTemplate;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class OpenMetadataAuthenticationProvider implements AuthenticationProvider {
@Override
public AuthenticationProvider create(OpenMetadataServerConnection iConfig) {
return null;
}
@Override
public String authToken() {
return null;
}
@Override
public String getAccessToken() {
return null;
}
@Override
public void apply(RequestTemplate requestTemplate) {}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security.factory;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
import org.openmetadata.client.security.GoogleAuthenticationProvider;
import org.openmetadata.client.security.NoOpAuthenticationProvider;
import org.openmetadata.client.security.OktaAuthenticationProvider;
import org.openmetadata.client.security.interfaces.AuthenticationProvider;
public class AuthenticationProviderFactory {
public AuthenticationProvider getAuthProvider(OpenMetadataServerConnection serverConfig) {
switch (serverConfig.getAuthProvider()) {
case NO_AUTH:
return new NoOpAuthenticationProvider();
case GOOGLE:
return new GoogleAuthenticationProvider(serverConfig);
case OKTA:
return new OktaAuthenticationProvider(serverConfig);
case AUTH_0:
case CUSTOM_OIDC:
case AZURE:
case OPENMETADATA:
return null;
}
return null;
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security.interfaces;
import feign.RequestInterceptor;
import org.openmetadata.catalog.services.connections.metadata.OpenMetadataServerConnection;
public interface AuthenticationProvider extends RequestInterceptor {
AuthenticationProvider create(OpenMetadataServerConnection iConfig);
String authToken();
String getAccessToken();
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2021 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openmetadata.client.security.interfaces;
import feign.*;
import io.swagger.client.ApiClient;
import java.util.Map;
import org.openmetadata.client.model.OktaAccessTokenResponse;
public interface OktaAccessTokenApi extends ApiClient.Api {
@RequestLine(
"POST /v1/token?grant_type={grant_type}&scope={scope}&client_assertion_type={client_assertion_type}&client_assertion={client_assertion}")
@Headers({
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
OktaAccessTokenResponse getAccessToken(
@Param("grant_type") String grant_type,
@Param("scope") String scope,
@Param("client_assertion_type") String client_assertion_type,
@Param("client_assertion") String client_assertion);
@RequestLine(
"POST /v1/token?grant_type={grant_type}&scope={scope}&client_assertion_type={client_assertion_type}&client_assertion={client_assertion}")
@Headers({
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
})
OktaAccessTokenResponse 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);
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>catalog</artifactId>
<groupId>org.openmetadata</groupId>
<version>0.11.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>openmetadata-clients</artifactId>
<packaging>pom</packaging>
<modules>
<module>openmetadata-java-client</module>
</modules>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>

View File

@ -0,0 +1,40 @@
package org.openmetadata.core.util;
import java.io.InputStream;
import java.util.Properties;
import java.util.regex.Pattern;
import org.openmetadata.catalog.api.CatalogVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class VersionUtils {
private static final Logger LOG = LoggerFactory.getLogger(CommonUtil.class);
private VersionUtils() {}
public static CatalogVersion getCatalogVersion(String resourceName) {
CatalogVersion catalogVersion = new CatalogVersion();
try {
InputStream fileInput = VersionUtils.class.getResourceAsStream(resourceName);
Properties props = new Properties();
props.load(fileInput);
catalogVersion.setVersion(props.getProperty("version", "unknown"));
catalogVersion.setRevision(props.getProperty("revision", "unknown"));
String timestampAsString = props.getProperty("timestamp");
Long timestamp = timestampAsString != null ? Long.valueOf(timestampAsString) : null;
catalogVersion.setTimestamp(timestamp);
} catch (Exception ie) {
LOG.warn("Failed to read catalog version file");
}
return catalogVersion;
}
public static String getVersionFromString(String input) {
if (input.contains("-")) {
return input.split(Pattern.quote("-"))[0];
} else {
throw new IllegalArgumentException("Invalid Version Given :" + input);
}
}
}