From 61b31676d004a50db829e4041df0bb3fd288b19f Mon Sep 17 00:00:00 2001 From: FANNG Date: Mon, 9 Dec 2024 14:30:02 +0800 Subject: [PATCH] [#5622] feat(client): support credential client in Gravition java client (#5753) ### What changes were proposed in this pull request? support credential client in Gravition java client ### Why are the changes needed? Fix: #5622 ### Does this PR introduce _any_ user-facing change? no ### How was this patch tested? add UT and test the overall flow in POC --- .../gravitino/credential/Credential.java | 10 + .../credential/GCSTokenCredential.java | 36 +++- .../credential/OSSTokenCredential.java | 40 +++- .../credential/S3SecretKeyCredential.java | 33 +++- .../credential/S3TokenCredential.java | 46 +++-- ...org.apache.gravitino.credential.Credential | 23 +++ .../gravitino/client/BaseSchemaCatalog.java | 5 + .../gravitino/client/ErrorHandlers.java | 47 +++++ .../gravitino/client/FilesetCatalog.java | 15 +- .../gravitino/client/GenericFileset.java | 17 +- .../MetadataObjectCredentialOperations.java | 62 +++++++ .../client/TestSupportCredentials.java | 175 ++++++++++++++++++ .../credential/CredentialFactory.java | 69 +++++++ .../dto/credential/CredentialDTO.java | 131 +++++++++++++ .../dto/responses/CredentialResponse.java | 62 +++++++ .../gravitino/dto/util/DTOConverters.java | 52 ++++++ .../credential/TestCredentialDTO.java | 50 +++++ .../credential/TestCredentialFactory.java | 116 ++++++++++++ .../TestCredentialPropertiesUtils.java | 2 +- .../credential/DummyCredentialProvider.java | 6 + .../extension/DummyCredentialProvider.java | 6 + 21 files changed, 972 insertions(+), 31 deletions(-) create mode 100644 api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential create mode 100644 clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectCredentialOperations.java create mode 100644 clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportCredentials.java create mode 100644 common/src/main/java/org/apache/gravitino/credential/CredentialFactory.java create mode 100644 common/src/main/java/org/apache/gravitino/dto/credential/CredentialDTO.java create mode 100644 common/src/main/java/org/apache/gravitino/dto/responses/CredentialResponse.java create mode 100644 common/src/test/java/org/apache/gravitino/credential/TestCredentialDTO.java create mode 100644 common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java diff --git a/api/src/main/java/org/apache/gravitino/credential/Credential.java b/api/src/main/java/org/apache/gravitino/credential/Credential.java index b2fdb1971e6..7ba9c422065 100644 --- a/api/src/main/java/org/apache/gravitino/credential/Credential.java +++ b/api/src/main/java/org/apache/gravitino/credential/Credential.java @@ -52,6 +52,16 @@ public interface Credential { */ Map credentialInfo(); + /** + * Initialize the credential with the credential information. + * + *

This method is invoked to deserialize the credential in client side. + * + * @param credentialInfo The credential information from {@link #credentialInfo}. + * @param expireTimeInMs The expire-time from {@link #expireTimeInMs()}. + */ + void initialize(Map credentialInfo, long expireTimeInMs); + /** * Converts the credential to properties to transfer the credential though API. * diff --git a/api/src/main/java/org/apache/gravitino/credential/GCSTokenCredential.java b/api/src/main/java/org/apache/gravitino/credential/GCSTokenCredential.java index 59dc3b24637..d55799c52a9 100644 --- a/api/src/main/java/org/apache/gravitino/credential/GCSTokenCredential.java +++ b/api/src/main/java/org/apache/gravitino/credential/GCSTokenCredential.java @@ -33,20 +33,25 @@ public class GCSTokenCredential implements Credential { /** GCS credential property, token name. */ public static final String GCS_TOKEN_NAME = "token"; - private final String token; - private final long expireMs; + private String token; + private long expireTimeInMs; /** * @param token The GCS token. - * @param expireMs The GCS token expire time at ms. + * @param expireTimeInMs The GCS token expire time at ms. */ - public GCSTokenCredential(String token, long expireMs) { - Preconditions.checkArgument( - StringUtils.isNotBlank(token), "GCS session token should not be null"); + public GCSTokenCredential(String token, long expireTimeInMs) { + validate(token, expireTimeInMs); this.token = token; - this.expireMs = expireMs; + this.expireTimeInMs = expireTimeInMs; } + /** + * This is the constructor that is used by credential factory to create an instance of credential + * according to the credential information. + */ + public GCSTokenCredential() {} + @Override public String credentialType() { return GCS_TOKEN_CREDENTIAL_TYPE; @@ -54,7 +59,7 @@ public String credentialType() { @Override public long expireTimeInMs() { - return expireMs; + return expireTimeInMs; } @Override @@ -62,6 +67,14 @@ public Map credentialInfo() { return (new ImmutableMap.Builder()).put(GCS_TOKEN_NAME, token).build(); } + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + String token = credentialInfo.get(GCS_TOKEN_NAME); + validate(token, expireTimeInMs); + this.token = token; + this.expireTimeInMs = expireTimeInMs; + } + /** * Get GCS token. * @@ -70,4 +83,11 @@ public Map credentialInfo() { public String token() { return token; } + + private void validate(String token, long expireTimeInMs) { + Preconditions.checkArgument( + StringUtils.isNotBlank(token), "GCS session token should not be empty"); + Preconditions.checkArgument( + expireTimeInMs > 0, "The expire time of GcsTokenCredential should great than 0"); + } } diff --git a/api/src/main/java/org/apache/gravitino/credential/OSSTokenCredential.java b/api/src/main/java/org/apache/gravitino/credential/OSSTokenCredential.java index 308d76a8dd2..edf23f207b7 100644 --- a/api/src/main/java/org/apache/gravitino/credential/OSSTokenCredential.java +++ b/api/src/main/java/org/apache/gravitino/credential/OSSTokenCredential.java @@ -36,10 +36,10 @@ public class OSSTokenCredential implements Credential { /** OSS security token. */ public static final String GRAVITINO_OSS_TOKEN = "oss-security-token"; - private final String accessKeyId; - private final String secretAccessKey; - private final String securityToken; - private final long expireTimeInMS; + private String accessKeyId; + private String secretAccessKey; + private String securityToken; + private long expireTimeInMS; /** * Constructs an instance of {@link OSSTokenCredential} with secret key and token. @@ -64,6 +64,12 @@ public OSSTokenCredential( this.expireTimeInMS = expireTimeInMS; } + /** + * This is the constructor that is used by credential factory to create an instance of credential + * according to the credential information. + */ + public OSSTokenCredential() {} + @Override public String credentialType() { return OSS_TOKEN_CREDENTIAL_TYPE; @@ -83,6 +89,20 @@ public Map credentialInfo() { .build(); } + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + String accessKeyId = credentialInfo.get(GRAVITINO_OSS_SESSION_ACCESS_KEY_ID); + String secretAccessKey = credentialInfo.get(GRAVITINO_OSS_SESSION_SECRET_ACCESS_KEY); + String securityToken = credentialInfo.get(GRAVITINO_OSS_TOKEN); + + validate(accessKeyId, secretAccessKey, securityToken, expireTimeInMs); + + this.accessKeyId = accessKeyId; + this.secretAccessKey = secretAccessKey; + this.securityToken = securityToken; + this.expireTimeInMS = expireTimeInMs; + } + /** * Get oss access key ID. * @@ -109,4 +129,16 @@ public String secretAccessKey() { public String securityToken() { return securityToken; } + + private void validate( + String accessKeyId, String secretAccessKey, String sessionToken, long expireTimeInMs) { + Preconditions.checkArgument( + StringUtils.isNotBlank(accessKeyId), "S3 access key Id should not be empty"); + Preconditions.checkArgument( + StringUtils.isNotBlank(secretAccessKey), "S3 secret access key should not be empty"); + Preconditions.checkArgument( + StringUtils.isNotBlank(sessionToken), "S3 session token should not be empty"); + Preconditions.checkArgument( + expireTimeInMs > 0, "The expire time of S3TokenCredential should great than 0"); + } } diff --git a/api/src/main/java/org/apache/gravitino/credential/S3SecretKeyCredential.java b/api/src/main/java/org/apache/gravitino/credential/S3SecretKeyCredential.java index 3063d5615f6..d9b2b609250 100644 --- a/api/src/main/java/org/apache/gravitino/credential/S3SecretKeyCredential.java +++ b/api/src/main/java/org/apache/gravitino/credential/S3SecretKeyCredential.java @@ -22,6 +22,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; /** S3 secret key credential. */ public class S3SecretKeyCredential implements Credential { @@ -33,8 +34,8 @@ public class S3SecretKeyCredential implements Credential { /** The static secret access key used to access S3 data. */ public static final String GRAVITINO_S3_STATIC_SECRET_ACCESS_KEY = "s3-secret-access-key"; - private final String accessKeyId; - private final String secretAccessKey; + private String accessKeyId; + private String secretAccessKey; /** * Constructs an instance of {@link S3SecretKeyCredential} with the static S3 access key ID and @@ -44,13 +45,17 @@ public class S3SecretKeyCredential implements Credential { * @param secretAccessKey The S3 static secret access key. */ public S3SecretKeyCredential(String accessKeyId, String secretAccessKey) { - Preconditions.checkNotNull(accessKeyId, "S3 access key Id should not null"); - Preconditions.checkNotNull(secretAccessKey, "S3 secret access key should not null"); - + validate(accessKeyId, secretAccessKey, 0); this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; } + /** + * This is the constructor that is used by credential factory to create an instance of credential + * according to the credential information. + */ + public S3SecretKeyCredential() {} + @Override public String credentialType() { return S3_SECRET_KEY_CREDENTIAL_TYPE; @@ -69,6 +74,15 @@ public Map credentialInfo() { .build(); } + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + String accessKeyId = credentialInfo.get(GRAVITINO_S3_STATIC_ACCESS_KEY_ID); + String secretAccessKey = credentialInfo.get(GRAVITINO_S3_STATIC_SECRET_ACCESS_KEY); + validate(accessKeyId, secretAccessKey, expireTimeInMs); + this.accessKeyId = accessKeyId; + this.secretAccessKey = secretAccessKey; + } + /** * Get S3 static access key ID. * @@ -86,4 +100,13 @@ public String accessKeyId() { public String secretAccessKey() { return secretAccessKey; } + + private void validate(String accessKeyId, String secretAccessKey, long expireTimeInMs) { + Preconditions.checkArgument( + StringUtils.isNotBlank(accessKeyId), "S3 access key Id should not empty"); + Preconditions.checkArgument( + StringUtils.isNotBlank(secretAccessKey), "S3 secret access key should not empty"); + Preconditions.checkArgument( + expireTimeInMs == 0, "The expire time of S3SecretKeyCredential is not 0"); + } } diff --git a/api/src/main/java/org/apache/gravitino/credential/S3TokenCredential.java b/api/src/main/java/org/apache/gravitino/credential/S3TokenCredential.java index d30a7e14079..89d7de760fa 100644 --- a/api/src/main/java/org/apache/gravitino/credential/S3TokenCredential.java +++ b/api/src/main/java/org/apache/gravitino/credential/S3TokenCredential.java @@ -36,10 +36,10 @@ public class S3TokenCredential implements Credential { /** S3 session token. */ public static final String GRAVITINO_S3_TOKEN = "s3-session-token"; - private final String accessKeyId; - private final String secretAccessKey; - private final String sessionToken; - private final long expireTimeInMS; + private String accessKeyId; + private String secretAccessKey; + private String sessionToken; + private long expireTimeInMS; /** * Constructs an instance of {@link S3SecretKeyCredential} with session secret key and token. @@ -51,19 +51,19 @@ public class S3TokenCredential implements Credential { */ public S3TokenCredential( String accessKeyId, String secretAccessKey, String sessionToken, long expireTimeInMS) { - Preconditions.checkArgument( - StringUtils.isNotBlank(accessKeyId), "S3 access key Id should not be empty"); - Preconditions.checkArgument( - StringUtils.isNotBlank(secretAccessKey), "S3 secret access key should not be empty"); - Preconditions.checkArgument( - StringUtils.isNotBlank(sessionToken), "S3 session token should not be empty"); - + validate(accessKeyId, secretAccessKey, sessionToken, expireTimeInMS); this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; this.sessionToken = sessionToken; this.expireTimeInMS = expireTimeInMS; } + /** + * This is the constructor that is used by credential factory to create an instance of credential + * according to the credential information. + */ + public S3TokenCredential() {} + @Override public String credentialType() { return S3_TOKEN_CREDENTIAL_TYPE; @@ -83,6 +83,18 @@ public Map credentialInfo() { .build(); } + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + String accessKeyId = credentialInfo.get(GRAVITINO_S3_SESSION_ACCESS_KEY_ID); + String secretAccessKey = credentialInfo.get(GRAVITINO_S3_SESSION_SECRET_ACCESS_KEY); + String sessionToken = credentialInfo.get(GRAVITINO_S3_TOKEN); + validate(accessKeyId, secretAccessKey, sessionToken, expireTimeInMs); + this.accessKeyId = accessKeyId; + this.secretAccessKey = secretAccessKey; + this.sessionToken = sessionToken; + this.expireTimeInMS = expireTimeInMs; + } + /** * Get S3 session access key ID. * @@ -109,4 +121,16 @@ public String secretAccessKey() { public String sessionToken() { return sessionToken; } + + private void validate( + String accessKeyId, String secretAccessKey, String sessionToken, long expireTimeInMs) { + Preconditions.checkArgument( + StringUtils.isNotBlank(accessKeyId), "S3 access key Id should not be empty"); + Preconditions.checkArgument( + StringUtils.isNotBlank(secretAccessKey), "S3 secret access key should not be empty"); + Preconditions.checkArgument( + StringUtils.isNotBlank(sessionToken), "S3 session token should not be empty"); + Preconditions.checkArgument( + expireTimeInMs > 0, "The expire time of S3TokenCredential should great than 0"); + } } diff --git a/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential b/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential new file mode 100644 index 00000000000..91d061be7b2 --- /dev/null +++ b/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +org.apache.gravitino.credential.S3TokenCredential +org.apache.gravitino.credential.S3SecretKeyCredential +org.apache.gravitino.credential.GCSTokenCredential +org.apache.gravitino.credential.OSSTokenCredential diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java b/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java index 9359ea439b0..d0a4d9db121 100644 --- a/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java @@ -55,6 +55,7 @@ */ abstract class BaseSchemaCatalog extends CatalogDTO implements Catalog, SupportsSchemas, SupportsTags, SupportsRoles { + /** The REST client to send the requests. */ protected final RESTClient restClient; @@ -63,6 +64,7 @@ abstract class BaseSchemaCatalog extends CatalogDTO private final MetadataObjectTagOperations objectTagOperations; private final MetadataObjectRoleOperations objectRoleOperations; + protected final MetadataObjectCredentialOperations objectCredentialOperations; BaseSchemaCatalog( Namespace catalogNamespace, @@ -88,6 +90,9 @@ abstract class BaseSchemaCatalog extends CatalogDTO new MetadataObjectTagOperations(catalogNamespace.level(0), metadataObject, restClient); this.objectRoleOperations = new MetadataObjectRoleOperations(catalogNamespace.level(0), metadataObject, restClient); + this.objectCredentialOperations = + new MetadataObjectCredentialOperations( + catalogNamespace.level(0), metadataObject, restClient); } @Override diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java index 3dcf6672ae6..776300a5a68 100644 --- a/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/ErrorHandlers.java @@ -42,6 +42,7 @@ import org.apache.gravitino.exceptions.MetalakeInUseException; import org.apache.gravitino.exceptions.MetalakeNotInUseException; import org.apache.gravitino.exceptions.NoSuchCatalogException; +import org.apache.gravitino.exceptions.NoSuchCredentialException; import org.apache.gravitino.exceptions.NoSuchFilesetException; import org.apache.gravitino.exceptions.NoSuchGroupException; import org.apache.gravitino.exceptions.NoSuchMetadataObjectException; @@ -201,6 +202,15 @@ public static Consumer tagErrorHandler() { return TagErrorHandler.INSTANCE; } + /** + * Creates an error handler specific to credential operations. + * + * @return A Consumer representing the credential error handler. + */ + public static Consumer credentialErrorHandler() { + return CredentialErrorHandler.INSTANCE; + } + /** * Creates an error handler specific to Owner operations. * @@ -858,6 +868,43 @@ public void accept(ErrorResponse errorResponse) { } } + /** Error handler specific to Credential operations. */ + @SuppressWarnings("FormatStringAnnotation") + private static class CredentialErrorHandler extends RestErrorHandler { + + private static final CredentialErrorHandler INSTANCE = new CredentialErrorHandler(); + + @Override + public void accept(ErrorResponse errorResponse) { + String errorMessage = formatErrorMessage(errorResponse); + + switch (errorResponse.getCode()) { + case ErrorConstants.ILLEGAL_ARGUMENTS_CODE: + throw new IllegalArgumentException(errorMessage); + + case ErrorConstants.NOT_FOUND_CODE: + if (errorResponse.getType().equals(NoSuchMetalakeException.class.getSimpleName())) { + throw new NoSuchMetalakeException(errorMessage); + } else if (errorResponse + .getType() + .equals(NoSuchCredentialException.class.getSimpleName())) { + throw new NoSuchCredentialException(errorMessage); + } else { + throw new NotFoundException(errorMessage); + } + + case ErrorConstants.NOT_IN_USE_CODE: + throw new MetalakeNotInUseException(errorMessage); + + case ErrorConstants.INTERNAL_ERROR_CODE: + throw new RuntimeException(errorMessage); + + default: + super.accept(errorResponse); + } + } + } + /** Error handler specific to Tag operations. */ @SuppressWarnings("FormatStringAnnotation") private static class TagErrorHandler extends RestErrorHandler { diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/FilesetCatalog.java b/clients/client-java/src/main/java/org/apache/gravitino/client/FilesetCatalog.java index a57482ff5e9..a58075aaf1a 100644 --- a/clients/client-java/src/main/java/org/apache/gravitino/client/FilesetCatalog.java +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/FilesetCatalog.java @@ -31,6 +31,8 @@ import org.apache.gravitino.NameIdentifier; import org.apache.gravitino.Namespace; import org.apache.gravitino.audit.CallerContext; +import org.apache.gravitino.credential.Credential; +import org.apache.gravitino.credential.SupportsCredentials; import org.apache.gravitino.dto.AuditDTO; import org.apache.gravitino.dto.CatalogDTO; import org.apache.gravitino.dto.requests.FilesetCreateRequest; @@ -52,7 +54,8 @@ * example, schemas and filesets list, creation, update and deletion. A Fileset catalog is under the * metalake. */ -class FilesetCatalog extends BaseSchemaCatalog implements org.apache.gravitino.file.FilesetCatalog { +class FilesetCatalog extends BaseSchemaCatalog + implements org.apache.gravitino.file.FilesetCatalog, SupportsCredentials { FilesetCatalog( Namespace namespace, @@ -265,6 +268,16 @@ public String getFileLocation(NameIdentifier ident, String subPath) } } + @Override + public SupportsCredentials supportsCredentials() throws UnsupportedOperationException { + return this; + } + + @Override + public Credential[] getCredentials() { + return objectCredentialOperations.getCredentials(); + } + @VisibleForTesting static String formatFilesetRequestPath(Namespace ns) { Namespace schemaNs = Namespace.of(ns.level(0), ns.level(1)); diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java index 68eda6985ab..6e587b11554 100644 --- a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java @@ -27,6 +27,8 @@ import org.apache.gravitino.MetadataObjects; import org.apache.gravitino.Namespace; import org.apache.gravitino.authorization.SupportsRoles; +import org.apache.gravitino.credential.Credential; +import org.apache.gravitino.credential.SupportsCredentials; import org.apache.gravitino.dto.file.FilesetDTO; import org.apache.gravitino.exceptions.NoSuchTagException; import org.apache.gravitino.file.Fileset; @@ -34,12 +36,13 @@ import org.apache.gravitino.tag.Tag; /** Represents a generic fileset. */ -class GenericFileset implements Fileset, SupportsTags, SupportsRoles { +class GenericFileset implements Fileset, SupportsTags, SupportsRoles, SupportsCredentials { private final FilesetDTO filesetDTO; private final MetadataObjectTagOperations objectTagOperations; private final MetadataObjectRoleOperations objectRoleOperations; + private final MetadataObjectCredentialOperations objectCredentialOperations; GenericFileset(FilesetDTO filesetDTO, RESTClient restClient, Namespace filesetNs) { this.filesetDTO = filesetDTO; @@ -50,6 +53,8 @@ class GenericFileset implements Fileset, SupportsTags, SupportsRoles { new MetadataObjectTagOperations(filesetNs.level(0), filesetObject, restClient); this.objectRoleOperations = new MetadataObjectRoleOperations(filesetNs.level(0), filesetObject, restClient); + this.objectCredentialOperations = + new MetadataObjectCredentialOperations(filesetNs.level(0), filesetObject, restClient); } @Override @@ -93,6 +98,11 @@ public SupportsRoles supportsRoles() { return this; } + @Override + public SupportsCredentials supportsCredentials() { + return this; + } + @Override public String[] listTags() { return objectTagOperations.listTags(); @@ -118,6 +128,11 @@ public String[] listBindingRoleNames() { return objectRoleOperations.listBindingRoleNames(); } + @Override + public Credential[] getCredentials() { + return objectCredentialOperations.getCredentials(); + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectCredentialOperations.java b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectCredentialOperations.java new file mode 100644 index 00000000000..b11fd9caf20 --- /dev/null +++ b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectCredentialOperations.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.client; + +import java.util.Collections; +import java.util.Locale; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.credential.Credential; +import org.apache.gravitino.credential.SupportsCredentials; +import org.apache.gravitino.dto.responses.CredentialResponse; +import org.apache.gravitino.dto.util.DTOConverters; + +/** + * The implementation of {@link SupportsCredentials}. This interface will be composited into + * catalog, table, fileset to provide credential operations for these metadata objects + */ +class MetadataObjectCredentialOperations implements SupportsCredentials { + + private final RESTClient restClient; + + private final String credentialRequestPath; + + MetadataObjectCredentialOperations( + String metalakeName, MetadataObject metadataObject, RESTClient restClient) { + this.restClient = restClient; + this.credentialRequestPath = + String.format( + "api/metalakes/%s/objects/%s/%s/credentials", + metalakeName, + metadataObject.type().name().toLowerCase(Locale.ROOT), + metadataObject.fullName()); + } + + @Override + public Credential[] getCredentials() { + CredentialResponse resp = + restClient.get( + credentialRequestPath, + CredentialResponse.class, + Collections.emptyMap(), + ErrorHandlers.credentialErrorHandler()); + + resp.validate(); + return DTOConverters.fromDTO(resp.getCredentials()); + } +} diff --git a/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportCredentials.java b/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportCredentials.java new file mode 100644 index 00000000000..7b0817c8bb5 --- /dev/null +++ b/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportCredentials.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.client; + +import static org.apache.hc.core5.http.HttpStatus.SC_INTERNAL_SERVER_ERROR; +import static org.apache.hc.core5.http.HttpStatus.SC_NOT_FOUND; +import static org.apache.hc.core5.http.HttpStatus.SC_OK; + +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.Collections; +import java.util.Locale; +import org.apache.gravitino.Catalog; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.MetadataObjects; +import org.apache.gravitino.Namespace; +import org.apache.gravitino.credential.Credential; +import org.apache.gravitino.credential.GCSTokenCredential; +import org.apache.gravitino.credential.S3SecretKeyCredential; +import org.apache.gravitino.credential.SupportsCredentials; +import org.apache.gravitino.dto.AuditDTO; +import org.apache.gravitino.dto.credential.CredentialDTO; +import org.apache.gravitino.dto.file.FilesetDTO; +import org.apache.gravitino.dto.responses.CredentialResponse; +import org.apache.gravitino.dto.responses.ErrorResponse; +import org.apache.gravitino.dto.util.DTOConverters; +import org.apache.gravitino.exceptions.NoSuchCredentialException; +import org.apache.gravitino.file.Fileset; +import org.apache.hc.core5.http.Method; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TestSupportCredentials extends TestBase { + + private static final String METALAKE_NAME = "metalake"; + + private static Catalog filesetCatalog; + + private static Fileset genericFileset; + + @BeforeAll + public static void setUp() throws Exception { + TestBase.setUp(); + TestGravitinoMetalake.createMetalake(client, METALAKE_NAME); + + filesetCatalog = + new FilesetCatalog( + Namespace.of(METALAKE_NAME), + "catalog2", + Catalog.Type.FILESET, + "test", + "comment", + Collections.emptyMap(), + AuditDTO.builder().build(), + client.restClient()); + + genericFileset = + new GenericFileset( + FilesetDTO.builder() + .name("fileset1") + .comment("comment1") + .type(Fileset.Type.EXTERNAL) + .storageLocation("s3://bucket/path") + .properties(Collections.emptyMap()) + .audit(AuditDTO.builder().withCreator("test").build()) + .build(), + client.restClient(), + Namespace.of(METALAKE_NAME, "catalog1", "schema1")); + } + + @Test + public void testGetCredentialsForCatalog() throws JsonProcessingException { + testGetCredentials( + filesetCatalog.supportsCredentials(), + MetadataObjects.of(null, filesetCatalog.name(), MetadataObject.Type.CATALOG)); + } + + @Test + public void testGetCredentialsForFileset() throws JsonProcessingException { + testGetCredentials( + genericFileset.supportsCredentials(), + MetadataObjects.of("catalog1.schema1", genericFileset.name(), MetadataObject.Type.FILESET)); + } + + private void testGetCredentials( + SupportsCredentials supportsCredentials, MetadataObject metadataObject) + throws JsonProcessingException { + String path = + "/api/metalakes/" + + METALAKE_NAME + + "/objects/" + + metadataObject.type().name().toLowerCase(Locale.ROOT) + + "/" + + metadataObject.fullName() + + "/credentials"; + + S3SecretKeyCredential secretKeyCredential = + new S3SecretKeyCredential("access-id", "secret-key"); + GCSTokenCredential gcsTokenCredential = new GCSTokenCredential("token", 100); + + // Return one credential + CredentialResponse resp = + new CredentialResponse(DTOConverters.toDTO(new Credential[] {secretKeyCredential})); + buildMockResource(Method.GET, path, null, resp, SC_OK); + + Credential[] credentials = supportsCredentials.getCredentials(); + Assertions.assertEquals(1, credentials.length); + + Assertions.assertTrue(credentials[0] instanceof S3SecretKeyCredential); + S3SecretKeyCredential s3CredentialInClient = (S3SecretKeyCredential) credentials[0]; + Assertions.assertEquals("access-id", s3CredentialInClient.accessKeyId()); + Assertions.assertEquals("secret-key", s3CredentialInClient.secretAccessKey()); + Assertions.assertEquals(0, s3CredentialInClient.expireTimeInMs()); + + // Return multi credentials + resp = + new CredentialResponse( + DTOConverters.toDTO(new Credential[] {secretKeyCredential, gcsTokenCredential})); + buildMockResource(Method.GET, path, null, resp, SC_OK); + + credentials = supportsCredentials.getCredentials(); + Assertions.assertEquals(2, credentials.length); + + Assertions.assertTrue(credentials[0] instanceof S3SecretKeyCredential); + s3CredentialInClient = (S3SecretKeyCredential) credentials[0]; + Assertions.assertEquals("access-id", s3CredentialInClient.accessKeyId()); + Assertions.assertEquals("secret-key", s3CredentialInClient.secretAccessKey()); + Assertions.assertEquals(0, s3CredentialInClient.expireTimeInMs()); + + Assertions.assertTrue(credentials[1] instanceof GCSTokenCredential); + GCSTokenCredential gcsCredentialInClient = (GCSTokenCredential) credentials[1]; + Assertions.assertEquals("token", gcsCredentialInClient.token()); + Assertions.assertEquals(100, gcsCredentialInClient.expireTimeInMs()); + + // Return empty list + resp = new CredentialResponse(new CredentialDTO[0]); + buildMockResource(Method.GET, path, null, resp, SC_OK); + credentials = supportsCredentials.getCredentials(); + Assertions.assertEquals(0, credentials.length); + + // Test throw NoSuchCredentialException + ErrorResponse errorResp = + ErrorResponse.notFound(NoSuchCredentialException.class.getSimpleName(), "mock error"); + buildMockResource(Method.GET, path, null, errorResp, SC_NOT_FOUND); + + Throwable ex = + Assertions.assertThrows( + NoSuchCredentialException.class, () -> supportsCredentials.getCredentials()); + Assertions.assertTrue(ex.getMessage().contains("mock error")); + + // Test throw internal error + ErrorResponse errorResp1 = ErrorResponse.internalError("mock error"); + buildMockResource(Method.GET, path, null, errorResp1, SC_INTERNAL_SERVER_ERROR); + + Throwable ex1 = + Assertions.assertThrows(RuntimeException.class, () -> supportsCredentials.getCredentials()); + Assertions.assertTrue(ex1.getMessage().contains("mock error")); + } +} diff --git a/common/src/main/java/org/apache/gravitino/credential/CredentialFactory.java b/common/src/main/java/org/apache/gravitino/credential/CredentialFactory.java new file mode 100644 index 00000000000..0ba46301b95 --- /dev/null +++ b/common/src/main/java/org/apache/gravitino/credential/CredentialFactory.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.credential; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Streams; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.stream.Collectors; + +/** Create a specific credential according to the credential information. */ +public class CredentialFactory { + /** + * Creates a {@link Credential} instance based on the provided credential type, information, and + * expiration time. + * + * @param credentialType The type of the credential to be created. This string is used to look up + * the corresponding credential class. + * @param credentialInfo A {@link Map} containing key-value pairs of information needed to + * initialize the credential. + * @param expireTimeInMs The expiration time of the credential in milliseconds. + * @return A newly created and initialized {@link Credential} object. + */ + public static Credential create( + String credentialType, Map credentialInfo, long expireTimeInMs) { + Class credentialClz = lookupCredential(credentialType); + try { + Credential credential = credentialClz.getDeclaredConstructor().newInstance(); + credential.initialize(credentialInfo, expireTimeInMs); + return credential; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Class lookupCredential(String credentialType) { + ServiceLoader serviceLoader = ServiceLoader.load(Credential.class); + List> credentials = + Streams.stream(serviceLoader.iterator()) + .filter(credential -> credentialType.equalsIgnoreCase(credential.credentialType())) + .map(Credential::getClass) + .collect(Collectors.toList()); + if (credentials.isEmpty()) { + throw new RuntimeException("No credential found for: " + credentialType); + } else if (credentials.size() > 1) { + throw new RuntimeException("Multiple credential found for: " + credentialType); + } else { + return Iterables.getOnlyElement(credentials); + } + } +} diff --git a/common/src/main/java/org/apache/gravitino/dto/credential/CredentialDTO.java b/common/src/main/java/org/apache/gravitino/dto/credential/CredentialDTO.java new file mode 100644 index 00000000000..506ab5f1f5a --- /dev/null +++ b/common/src/main/java/org/apache/gravitino/dto/credential/CredentialDTO.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.dto.credential; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Objects; +import java.util.Map; +import org.apache.gravitino.credential.Credential; + +/** Represents a credential Data Transfer Object (DTO). */ +public class CredentialDTO implements Credential { + + @JsonProperty("credentialType") + private String credentialType; + + @JsonProperty("expireTimeInMs") + private long expireTimeInMs; + + @JsonProperty("credentialInfo") + private Map credentialInfo; + + private CredentialDTO() {} + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CredentialDTO)) { + return false; + } + + CredentialDTO credentialDTO = (CredentialDTO) o; + return Objects.equal(credentialType, credentialDTO.credentialType) + && Objects.equal(expireTimeInMs, credentialDTO.expireTimeInMs) + && Objects.equal(credentialInfo, credentialDTO.credentialInfo); + } + + @Override + public int hashCode() { + return Objects.hashCode(credentialType, expireTimeInMs, credentialInfo); + } + + @Override + public String credentialType() { + return credentialType; + } + + @Override + public long expireTimeInMs() { + return expireTimeInMs; + } + + @Override + public Map credentialInfo() { + return credentialInfo; + } + + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + throw new UnsupportedOperationException("CredentialDTO doesn't support initWithCredentialInfo"); + } + + /** @return a new builder for constructing a Credential DTO. */ + public static Builder builder() { + return new Builder(); + } + + /** Builder class for constructing CredentialDTO instances. */ + public static class Builder { + private final CredentialDTO credentialDTO; + + private Builder() { + credentialDTO = new CredentialDTO(); + } + + /** + * Sets the credential type. + * + * @param credentialType The specific credential type. + * @return The builder instance. + */ + public Builder withCredentialType(String credentialType) { + credentialDTO.credentialType = credentialType; + return this; + } + + /** + * Sets the credential expire time. + * + * @param expireTimeInMs The credential expire time. + * @return The builder instance. + */ + public Builder withExpireTimeInMs(long expireTimeInMs) { + credentialDTO.expireTimeInMs = expireTimeInMs; + return this; + } + + /** + * Sets the credential information. + * + * @param credentialInfo The specific credential information map. + * @return The builder instance. + */ + public Builder withCredentialInfo(Map credentialInfo) { + credentialDTO.credentialInfo = credentialInfo; + return this; + } + + /** @return The constructed credential DTO. */ + public CredentialDTO build() { + return credentialDTO; + } + } +} diff --git a/common/src/main/java/org/apache/gravitino/dto/responses/CredentialResponse.java b/common/src/main/java/org/apache/gravitino/dto/responses/CredentialResponse.java new file mode 100644 index 00000000000..88d904b7fdb --- /dev/null +++ b/common/src/main/java/org/apache/gravitino/dto/responses/CredentialResponse.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.dto.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.apache.gravitino.dto.credential.CredentialDTO; + +/** Represents a response for credentials. */ +@Getter +@ToString +@EqualsAndHashCode(callSuper = true) +public class CredentialResponse extends BaseResponse { + + @JsonProperty("credentials") + private final CredentialDTO[] credentials; + + /** + * Creates a new CredentialResponse. + * + * @param credentials The credentials. + */ + public CredentialResponse(CredentialDTO[] credentials) { + super(0); + this.credentials = credentials; + } + + /** + * This is the constructor that is used by Jackson deserializer to create an instance of + * CredentialResponse. + */ + public CredentialResponse() { + super(); + this.credentials = null; + } + + @Override + public void validate() throws IllegalArgumentException { + super.validate(); + + Preconditions.checkArgument(credentials != null, "\"credentials\" must not be null"); + } +} diff --git a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java index d12b141ff3a..254de8c3245 100644 --- a/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java +++ b/common/src/main/java/org/apache/gravitino/dto/util/DTOConverters.java @@ -36,6 +36,8 @@ import org.apache.gravitino.authorization.Role; import org.apache.gravitino.authorization.SecurableObject; import org.apache.gravitino.authorization.User; +import org.apache.gravitino.credential.Credential; +import org.apache.gravitino.credential.CredentialFactory; import org.apache.gravitino.dto.AuditDTO; import org.apache.gravitino.dto.CatalogDTO; import org.apache.gravitino.dto.MetalakeDTO; @@ -46,6 +48,7 @@ import org.apache.gravitino.dto.authorization.RoleDTO; import org.apache.gravitino.dto.authorization.SecurableObjectDTO; import org.apache.gravitino.dto.authorization.UserDTO; +import org.apache.gravitino.dto.credential.CredentialDTO; import org.apache.gravitino.dto.file.FilesetDTO; import org.apache.gravitino.dto.messaging.TopicDTO; import org.apache.gravitino.dto.rel.ColumnDTO; @@ -516,6 +519,31 @@ public static TagDTO toDTO(Tag tag, Optional inherited) { return builder.build(); } + /** + * Converts credentials to CredentialDTOs. + * + * @param credentials the credentials to be converted. + * @return The credential DTOs. + */ + public static CredentialDTO[] toDTO(Credential[] credentials) { + return Arrays.stream(credentials).map(DTOConverters::toDTO).toArray(CredentialDTO[]::new); + } + + /** + * Converts a Credential to a CredentialDTO. + * + * @param credential the credential to be converted. + * @return The credential DTO. + */ + public static CredentialDTO toDTO(Credential credential) { + CredentialDTO.Builder builder = + CredentialDTO.builder() + .withCredentialType(credential.credentialType()) + .withExpireTimeInMs(credential.expireTimeInMs()) + .withCredentialInfo(credential.credentialInfo()); + return builder.build(); + } + /** * Converts an Expression to an FunctionArg DTO. * @@ -881,6 +909,30 @@ public static Column[] fromDTOs(ColumnDTO[] columns) { return Arrays.stream(columns).map(DTOConverters::fromDTO).toArray(Column[]::new); } + /** + * Converts CredentialDTO array to credential array. + * + * @param credentials The credential DTO array to be converted. + * @return The credential array. + */ + public static Credential[] fromDTO(CredentialDTO[] credentials) { + if (ArrayUtils.isEmpty(credentials)) { + return new Credential[0]; + } + return Arrays.stream(credentials).map(DTOConverters::fromDTO).toArray(Credential[]::new); + } + + /** + * Converts a CredentialDTO to a credential. + * + * @param credential The credential DTO to be converted. + * @return The credential. + */ + public static Credential fromDTO(CredentialDTO credential) { + return CredentialFactory.create( + credential.credentialType(), credential.credentialInfo(), credential.expireTimeInMs()); + } + /** * Converts a ColumnDTO to a Column. * diff --git a/common/src/test/java/org/apache/gravitino/credential/TestCredentialDTO.java b/common/src/test/java/org/apache/gravitino/credential/TestCredentialDTO.java new file mode 100644 index 00000000000..88fb394e8be --- /dev/null +++ b/common/src/test/java/org/apache/gravitino/credential/TestCredentialDTO.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.credential; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.apache.gravitino.dto.credential.CredentialDTO; +import org.apache.gravitino.json.JsonUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCredentialDTO { + + @Test + public void testCredentialDTOSerDe() throws JsonProcessingException { + Map credentialInfo = + ImmutableMap.of( + S3SecretKeyCredential.GRAVITINO_S3_STATIC_ACCESS_KEY_ID, "access-key", + S3SecretKeyCredential.GRAVITINO_S3_STATIC_SECRET_ACCESS_KEY, "secret-key"); + + CredentialDTO credentialDTO = + CredentialDTO.builder() + .withCredentialType(S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE) + .withCredentialInfo(credentialInfo) + .withExpireTimeInMs(10) + .build(); + + String serJson = JsonUtils.objectMapper().writeValueAsString(credentialDTO); + CredentialDTO deserCredentialDTO = + JsonUtils.objectMapper().readValue(serJson, CredentialDTO.class); + Assertions.assertEquals(credentialDTO, deserCredentialDTO); + } +} diff --git a/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java b/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java new file mode 100644 index 00000000000..f5a83e0d3e9 --- /dev/null +++ b/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.gravitino.credential; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TestCredentialFactory { + + @Test + void testS3TokenCredential() { + Map s3TokenCredentialInfo = + ImmutableMap.of( + S3TokenCredential.GRAVITINO_S3_SESSION_ACCESS_KEY_ID, + "accessKeyId", + S3TokenCredential.GRAVITINO_S3_SESSION_SECRET_ACCESS_KEY, + "secretAccessKey", + S3TokenCredential.GRAVITINO_S3_TOKEN, + "token"); + long expireTime = 1000; + Credential s3TokenCredential = + CredentialFactory.create( + S3TokenCredential.S3_TOKEN_CREDENTIAL_TYPE, s3TokenCredentialInfo, expireTime); + Assertions.assertEquals( + S3TokenCredential.S3_TOKEN_CREDENTIAL_TYPE, s3TokenCredential.credentialType()); + Assertions.assertTrue(s3TokenCredential instanceof S3TokenCredential); + S3TokenCredential s3TokenCredential1 = (S3TokenCredential) s3TokenCredential; + Assertions.assertEquals("accessKeyId", s3TokenCredential1.accessKeyId()); + Assertions.assertEquals("secretAccessKey", s3TokenCredential1.secretAccessKey()); + Assertions.assertEquals("token", s3TokenCredential1.sessionToken()); + Assertions.assertEquals(expireTime, s3TokenCredential1.expireTimeInMs()); + } + + @Test + void testS3SecretKeyTokenCredential() { + Map s3SecretKeyCredentialInfo = + ImmutableMap.of( + S3SecretKeyCredential.GRAVITINO_S3_STATIC_ACCESS_KEY_ID, + "accessKeyId", + S3SecretKeyCredential.GRAVITINO_S3_STATIC_SECRET_ACCESS_KEY, + "secretAccessKey"); + long expireTime = 0; + Credential s3SecretKeyCredential = + CredentialFactory.create( + S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE, + s3SecretKeyCredentialInfo, + expireTime); + Assertions.assertEquals( + S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE, + s3SecretKeyCredential.credentialType()); + Assertions.assertTrue(s3SecretKeyCredential instanceof S3SecretKeyCredential); + S3SecretKeyCredential s3SecretKeyCredential1 = (S3SecretKeyCredential) s3SecretKeyCredential; + Assertions.assertEquals("accessKeyId", s3SecretKeyCredential1.accessKeyId()); + Assertions.assertEquals("secretAccessKey", s3SecretKeyCredential1.secretAccessKey()); + Assertions.assertEquals(expireTime, s3SecretKeyCredential1.expireTimeInMs()); + } + + @Test + void testGcsTokenCredential() { + Map gcsTokenCredentialInfo = + ImmutableMap.of(GCSTokenCredential.GCS_TOKEN_NAME, "accessToken"); + long expireTime = 100; + Credential gcsTokenCredential = + CredentialFactory.create( + GCSTokenCredential.GCS_TOKEN_CREDENTIAL_TYPE, gcsTokenCredentialInfo, expireTime); + Assertions.assertEquals( + GCSTokenCredential.GCS_TOKEN_CREDENTIAL_TYPE, gcsTokenCredential.credentialType()); + Assertions.assertTrue(gcsTokenCredential instanceof GCSTokenCredential); + GCSTokenCredential gcsTokenCredential1 = (GCSTokenCredential) gcsTokenCredential; + Assertions.assertEquals("accessToken", gcsTokenCredential1.token()); + Assertions.assertEquals(expireTime, gcsTokenCredential1.expireTimeInMs()); + } + + @Test + void testOSSTokenCredential() { + Map ossTokenCredentialInfo = + ImmutableMap.of( + OSSTokenCredential.GRAVITINO_OSS_SESSION_ACCESS_KEY_ID, + "access-id", + OSSTokenCredential.GRAVITINO_OSS_SESSION_SECRET_ACCESS_KEY, + "secret-key", + OSSTokenCredential.GRAVITINO_OSS_TOKEN, + "token"); + long expireTime = 100; + Credential ossTokenCredential = + CredentialFactory.create( + OSSTokenCredential.OSS_TOKEN_CREDENTIAL_TYPE, ossTokenCredentialInfo, expireTime); + Assertions.assertEquals( + OSSTokenCredential.OSS_TOKEN_CREDENTIAL_TYPE, ossTokenCredential.credentialType()); + Assertions.assertTrue(ossTokenCredential instanceof OSSTokenCredential); + OSSTokenCredential ossTokenCredential1 = (OSSTokenCredential) ossTokenCredential; + Assertions.assertEquals("access-id", ossTokenCredential1.accessKeyId()); + Assertions.assertEquals("secret-key", ossTokenCredential1.secretAccessKey()); + Assertions.assertEquals("token", ossTokenCredential1.securityToken()); + Assertions.assertEquals(expireTime, ossTokenCredential1.expireTimeInMs()); + } +} diff --git a/common/src/test/java/org/apache/gravitino/credential/TestCredentialPropertiesUtils.java b/common/src/test/java/org/apache/gravitino/credential/TestCredentialPropertiesUtils.java index 3b4571e01e9..8e52b1684f5 100644 --- a/common/src/test/java/org/apache/gravitino/credential/TestCredentialPropertiesUtils.java +++ b/common/src/test/java/org/apache/gravitino/credential/TestCredentialPropertiesUtils.java @@ -28,7 +28,7 @@ public class TestCredentialPropertiesUtils { @Test void testToIcebergProperties() { - S3TokenCredential s3TokenCredential = new S3TokenCredential("key", "secret", "token", 0); + S3TokenCredential s3TokenCredential = new S3TokenCredential("key", "secret", "token", 100); Map icebergProperties = CredentialPropertyUtils.toIcebergProperties(s3TokenCredential); Map expectedProperties = diff --git a/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java b/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java index 864635e96c9..83516edee0b 100644 --- a/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java +++ b/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Set; +import javax.ws.rs.NotSupportedException; import lombok.Getter; public class DummyCredentialProvider implements CredentialProvider { @@ -79,5 +80,10 @@ public Map credentialInfo() { return ImmutableMap.of( "writeLocation", writeLocations.toString(), "readLocation", readLocations.toString()); } + + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + throw new NotSupportedException(); + } } } diff --git a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java index 6b1e4c08710..5ed9d7b5864 100644 --- a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java +++ b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/extension/DummyCredentialProvider.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; +import javax.ws.rs.NotSupportedException; import org.apache.gravitino.credential.Credential; import org.apache.gravitino.credential.CredentialContext; import org.apache.gravitino.credential.CredentialProvider; @@ -45,6 +46,11 @@ public long expireTimeInMs() { public Map credentialInfo() { return new HashMap<>(); } + + @Override + public void initialize(Map credentialInfo, long expireTimeInMs) { + throw new NotSupportedException(); + } } @Override