This is an automated email from the ASF dual-hosted git repository.
yuqi4733 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 61b31676d [#5622] feat(client): support credential client in Gravition
java client (#5753)
61b31676d is described below
commit 61b31676d004a50db829e4041df0bb3fd288b19f
Author: FANNG <[email protected]>
AuthorDate: Mon Dec 9 14:30:02 2024 +0800
[#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
---
.../apache/gravitino/credential/Credential.java | 10 ++
.../gravitino/credential/GCSTokenCredential.java | 36 ++++-
.../gravitino/credential/OSSTokenCredential.java | 40 ++++-
.../credential/S3SecretKeyCredential.java | 33 +++-
.../gravitino/credential/S3TokenCredential.java | 46 ++++--
.../org.apache.gravitino.credential.Credential | 23 +++
.../apache/gravitino/client/BaseSchemaCatalog.java | 5 +
.../org/apache/gravitino/client/ErrorHandlers.java | 47 ++++++
.../apache/gravitino/client/FilesetCatalog.java | 15 +-
.../apache/gravitino/client/GenericFileset.java | 17 +-
.../client/MetadataObjectCredentialOperations.java | 62 ++++++++
.../gravitino/client/TestSupportCredentials.java | 175 +++++++++++++++++++++
.../gravitino/credential/CredentialFactory.java | 69 ++++++++
.../gravitino/dto/credential/CredentialDTO.java | 131 +++++++++++++++
.../dto/responses/CredentialResponse.java | 62 ++++++++
.../apache/gravitino/dto/util/DTOConverters.java | 52 ++++++
.../gravitino/credential/TestCredentialDTO.java | 50 ++++++
.../credential/TestCredentialFactory.java | 116 ++++++++++++++
.../credential/TestCredentialPropertiesUtils.java | 2 +-
.../credential/DummyCredentialProvider.java | 6 +
.../service/extension/DummyCredentialProvider.java | 6 +
21 files changed, 972 insertions(+), 31 deletions(-)
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 b2fdb1971..7ba9c4220 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<String, String> credentialInfo();
+ /**
+ * Initialize the credential with the credential information.
+ *
+ * <p>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<String, String> 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 59dc3b246..d55799c52 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 class GCSTokenCredential implements Credential {
@Override
public long expireTimeInMs() {
- return expireMs;
+ return expireTimeInMs;
}
@Override
@@ -62,6 +67,14 @@ public class GCSTokenCredential implements Credential {
return (new ImmutableMap.Builder<String, String>()).put(GCS_TOKEN_NAME,
token).build();
}
+ @Override
+ public void initialize(Map<String, String> 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 class GCSTokenCredential implements Credential {
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 308d76a8d..edf23f207 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 class OSSTokenCredential implements Credential {
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 class OSSTokenCredential implements Credential {
.build();
}
+ @Override
+ public void initialize(Map<String, String> 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 class OSSTokenCredential implements Credential {
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 3063d5615..d9b2b6092 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 @@ package org.apache.gravitino.credential;
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 class S3SecretKeyCredential implements Credential {
.build();
}
+ @Override
+ public void initialize(Map<String, String> 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 class S3SecretKeyCredential implements Credential {
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 d30a7e140..89d7de760 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 class S3TokenCredential implements Credential {
.build();
}
+ @Override
+ public void initialize(Map<String, String> 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 class S3TokenCredential implements Credential {
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 000000000..91d061be7
--- /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 9359ea439..d0a4d9db1 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 @@ import org.apache.gravitino.tag.Tag;
*/
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 3dcf6672a..776300a5a 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.MetalakeAlreadyExistsException;
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 class ErrorHandlers {
return TagErrorHandler.INSTANCE;
}
+ /**
+ * Creates an error handler specific to credential operations.
+ *
+ * @return A Consumer representing the credential error handler.
+ */
+ public static Consumer<ErrorResponse> credentialErrorHandler() {
+ return CredentialErrorHandler.INSTANCE;
+ }
+
/**
* Creates an error handler specific to Owner operations.
*
@@ -858,6 +868,43 @@ public class ErrorHandlers {
}
}
+ /** 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 a57482ff5..a58075aaf 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.Catalog;
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 @@ import org.apache.gravitino.rest.RESTUtils;
* 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 @@ class FilesetCatalog extends BaseSchemaCatalog implements
org.apache.gravitino.f
}
}
+ @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 68eda6985..6e587b115 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.MetadataObject;
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.SupportsTags;
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 @@ class GenericFileset implements Fileset, SupportsTags,
SupportsRoles {
return this;
}
+ @Override
+ public SupportsCredentials supportsCredentials() {
+ return this;
+ }
+
@Override
public String[] listTags() {
return objectTagOperations.listTags();
@@ -118,6 +128,11 @@ class GenericFileset implements Fileset, SupportsTags,
SupportsRoles {
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 000000000..b11fd9caf
--- /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 000000000..7b0817c8b
--- /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 000000000..0ba46301b
--- /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<String, String> credentialInfo, long
expireTimeInMs) {
+ Class<? extends Credential> 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<? extends Credential> lookupCredential(String
credentialType) {
+ ServiceLoader<Credential> serviceLoader =
ServiceLoader.load(Credential.class);
+ List<Class<? extends Credential>> 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 000000000..506ab5f1f
--- /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<String, String> 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<String, String> credentialInfo() {
+ return credentialInfo;
+ }
+
+ @Override
+ public void initialize(Map<String, String> 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<String, String> 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 000000000..88d904b7f
--- /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 d12b141ff..254de8c32 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.Privileges;
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.PrivilegeDTO;
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 class DTOConverters {
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 class DTOConverters {
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 000000000..88fb394e8
--- /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<String, String> 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 000000000..f5a83e0d3
--- /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<String, String> 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<String, String> 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<String, String> 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<String, String> 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 3b4571e01..8e52b1684 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<String, String> icebergProperties =
CredentialPropertyUtils.toIcebergProperties(s3TokenCredential);
Map<String, String> 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 864635e96..83516edee 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.base.Preconditions;
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 class DummyCredentialProvider implements
CredentialProvider {
return ImmutableMap.of(
"writeLocation", writeLocations.toString(), "readLocation",
readLocations.toString());
}
+
+ @Override
+ public void initialize(Map<String, String> 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 6b1e4c087..5ed9d7b58 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.io.IOException;
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 class DummyCredentialProvider implements
CredentialProvider {
public Map<String, String> credentialInfo() {
return new HashMap<>();
}
+
+ @Override
+ public void initialize(Map<String, String> credentialInfo, long
expireTimeInMs) {
+ throw new NotSupportedException();
+ }
}
@Override