diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/security/JwtFilter.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/security/JwtFilter.java index 61dfde20e21..9f1918023f3 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/security/JwtFilter.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/security/JwtFilter.java @@ -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; } diff --git a/openmetadata-clients/openmetadata-java-client/pom.xml b/openmetadata-clients/openmetadata-java-client/pom.xml new file mode 100644 index 00000000000..f43b3ae0ea4 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/pom.xml @@ -0,0 +1,138 @@ + + + + + openmetadata-clients + org.openmetadata + 0.11.0-SNAPSHOT + + 4.0.0 + + openmetadata-java-client + + 11 + ${java.version} + ${java.version} + 2.7.0 + 9.7.0 + + + + org.openmetadata + openmetadata-core + ${project.version} + + + io.swagger + swagger-codegen-maven-plugin + 3.0.0-rc1 + + + com.github.joschi.jackson + jackson-datatype-threetenbp + 2.6.4 + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + 1.0.2 + + + + io.github.openfeign + feign-jackson + ${feign-version} + + + io.github.openfeign + feign-slf4j + ${feign-version} + + + io.github.openfeign + feign-core + ${feign-version} + + + io.github.openfeign.form + feign-form + 3.8.0 + + + com.google.auth + google-auth-library-oauth2-http + 1.3.0 + + + + junit + junit + 4.13.2 + + + + + + + + com.theoryinpractise + googleformatter-maven-plugin + + + org.apache.maven.plugins + maven-source-plugin + ${maven-src-plugin.version} + + true + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + false + false + ${project.build.directory}/classes/catalog + VERSION + + + + generate-resources + + create-metadata + + + + + + io.swagger.codegen.v3 + swagger-codegen-maven-plugin + 3.0.16 + + + + generate + + + ${project.basedir}/../../catalog-rest-service/target/classes/assets/swagger.yaml + java + feign + true + true + + true + src/main/java/ + + ${project.build.directory}/generated-sources/swagger + + + + + + + + + diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/gateway/OpenMetadata.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/gateway/OpenMetadata.java new file mode 100644 index 00000000000..e87f5681d4d --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/gateway/OpenMetadata.java @@ -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 buildClient(Class clientClass) { + return apiClient.buildClient(clientClass); + } + + public T buildClient(Class clientClass, Class requestClass) { + updateRequestType(requestClass); + return apiClient.buildClient(clientClass); + } + + public void updateRequestType(Class requestClass) { + if (apiClient.getApiAuthorizations().containsKey(requestInterceptorKey)) { + apiClient.getApiAuthorizations().remove(requestInterceptorKey); + } + CustomRequestInterceptor 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()); + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/CustomRequestInterceptor.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/CustomRequestInterceptor.java new file mode 100644 index 00000000000..72a54a20006 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/CustomRequestInterceptor.java @@ -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 implements RequestInterceptor { + private static final Logger LOG = LoggerFactory.getLogger(OpenMetadata.class); + private final Class type; + ObjectMapper mapper; + + public CustomRequestInterceptor(ObjectMapper iMapper, Class 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 getType() { + return this.type; + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/OktaAccessTokenRequestInterceptor.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/OktaAccessTokenRequestInterceptor.java new file mode 100644 index 00000000000..580cec08699 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/interceptors/OktaAccessTokenRequestInterceptor.java @@ -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; + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaAccessTokenResponse.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaAccessTokenResponse.java new file mode 100644 index 00000000000..ea87902bae4 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaAccessTokenResponse.java @@ -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; + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaSSOConfig.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaSSOConfig.java new file mode 100644 index 00000000000..6eeb2893b63 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/model/OktaSSOConfig.java @@ -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 scopes = new ArrayList(); + + /** 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 getScopes() { + return scopes; + } + + /** Okta client scopes. */ + public void setScopes(List scopes) { + this.scopes = scopes; + } + + public OktaSSOConfig withScopes(List 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) ? "" : this.clientId)); + sb.append("clientSecret"); + sb.append('='); + sb.append(((this.clientSecret == null) ? "" : this.clientSecret)); + sb.append(','); + sb.append("authorizationServerURL"); + sb.append('='); + sb.append(((this.authorizationServerURL == null) ? "" : this.authorizationServerURL)); + sb.append(','); + sb.append(','); + sb.append("scopes"); + sb.append('='); + sb.append(((this.scopes == 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 CONSTANTS = + new HashMap(); + + 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; + } + } + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/Auth0AuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/Auth0AuthenticationProvider.java new file mode 100644 index 00000000000..77c07cd66f7 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/Auth0AuthenticationProvider.java @@ -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) {} +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/AzureAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/AzureAuthenticationProvider.java new file mode 100644 index 00000000000..1014130ce6f --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/AzureAuthenticationProvider.java @@ -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) {} +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/CustomOIDCAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/CustomOIDCAuthenticationProvider.java new file mode 100644 index 00000000000..654c1f5bb0f --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/CustomOIDCAuthenticationProvider.java @@ -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) {} +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/GoogleAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/GoogleAuthenticationProvider.java new file mode 100644 index 00000000000..c83b7e778f9 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/GoogleAuthenticationProvider.java @@ -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()); + } + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/NoOpAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/NoOpAuthenticationProvider.java new file mode 100644 index 00000000000..42b344818d3 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/NoOpAuthenticationProvider.java @@ -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; + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OktaAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OktaAuthenticationProvider.java new file mode 100644 index 00000000000..7cce0f6dd4f --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OktaAuthenticationProvider.java @@ -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()); + } + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OpenMetadataAuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OpenMetadataAuthenticationProvider.java new file mode 100644 index 00000000000..a928452e63f --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/OpenMetadataAuthenticationProvider.java @@ -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) {} +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/factory/AuthenticationProviderFactory.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/factory/AuthenticationProviderFactory.java new file mode 100644 index 00000000000..f2ff2b27084 --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/factory/AuthenticationProviderFactory.java @@ -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; + } +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/AuthenticationProvider.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/AuthenticationProvider.java new file mode 100644 index 00000000000..1f83aaf37ee --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/AuthenticationProvider.java @@ -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(); +} diff --git a/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/OktaAccessTokenApi.java b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/OktaAccessTokenApi.java new file mode 100644 index 00000000000..97c0320379f --- /dev/null +++ b/openmetadata-clients/openmetadata-java-client/src/main/java/org/openmetadata/client/security/interfaces/OktaAccessTokenApi.java @@ -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 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); +} diff --git a/openmetadata-clients/pom.xml b/openmetadata-clients/pom.xml new file mode 100644 index 00000000000..39b95f7d1f1 --- /dev/null +++ b/openmetadata-clients/pom.xml @@ -0,0 +1,21 @@ + + + + + catalog + org.openmetadata + 0.11.0-SNAPSHOT + + 4.0.0 + + openmetadata-clients + pom + + openmetadata-java-client + + + 11 + 11 + + diff --git a/openmetadata-core/src/main/java/org/openmetadata/core/util/VersionUtils.java b/openmetadata-core/src/main/java/org/openmetadata/core/util/VersionUtils.java new file mode 100644 index 00000000000..caaf2fb9ce4 --- /dev/null +++ b/openmetadata-core/src/main/java/org/openmetadata/core/util/VersionUtils.java @@ -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); + } + } +}