This is an automated email from the ASF dual-hosted git repository.
jshao 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 53bd22713 [#4992] support credential vending framework (#4995)
53bd22713 is described below
commit 53bd22713f879fdf4c7150c9ad3f099f06704209
Author: FANNG <[email protected]>
AuthorDate: Tue Oct 15 11:06:07 2024 +0800
[#4992] support credential vending framework (#4995)
### What changes were proposed in this pull request?
support credential vending framework
### Why are the changes needed?
Fix: #4992
### Does this PR introduce _any_ user-facing change?
no
### How was this patch tested?
1. add UT
2. propose a draft PR in #4966 , and could run pass S3 token with
Gravitino IcebergRESTServer
---
.../apache/gravitino/credential/Credential.java | 67 +++++++++++++++++
.../credential/CatalogCredentialContext.java | 38 ++++++++++
.../gravitino/credential/CredentialContext.java | 30 ++++++++
.../gravitino/credential/CredentialProvider.java | 56 +++++++++++++++
.../credential/CredentialProviderFactory.java | 69 ++++++++++++++++++
.../credential/PathBasedCredentialContext.java | 58 +++++++++++++++
.../credential/DummyCredentialProvider.java | 83 ++++++++++++++++++++++
.../credential/TestCredentialProvider.java | 54 ++++++++++++++
....apache.gravitino.credential.CredentialProvider | 19 +++++
9 files changed, 474 insertions(+)
diff --git a/api/src/main/java/org/apache/gravitino/credential/Credential.java
b/api/src/main/java/org/apache/gravitino/credential/Credential.java
new file mode 100644
index 000000000..b2fdb1971
--- /dev/null
+++ b/api/src/main/java/org/apache/gravitino/credential/Credential.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+/** Interface representing a credential with type, expiration time, and
additional information. */
+public interface Credential {
+ /** Credential type in the credential. */
+ String CREDENTIAL_TYPE = "credential-type";
+ /** Credential expire time in ms since the epoch. */
+ String EXPIRE_TIME_IN_MS = "expire-time-in-ms";
+
+ /**
+ * Returns the type of the credential. It should be the same as the
credential type of the
+ * credential provider.
+ *
+ * @return the credential type as a String.
+ */
+ String credentialType();
+
+ /**
+ * Returns the expiration time of the credential in milliseconds since the
epoch, 0 means not
+ * expire.
+ *
+ * @return the expiration time as a long.
+ */
+ long expireTimeInMs();
+
+ /**
+ * Returns credential information.
+ *
+ * @return a map of credential information.
+ */
+ Map<String, String> credentialInfo();
+
+ /**
+ * Converts the credential to properties to transfer the credential though
API.
+ *
+ * @return a map containing credential properties.
+ */
+ default Map<String, String> toProperties() {
+ return new ImmutableMap.Builder<String, String>()
+ .putAll(credentialInfo())
+ .put(CREDENTIAL_TYPE, credentialType())
+ .put(EXPIRE_TIME_IN_MS, String.valueOf(expireTimeInMs()))
+ .build();
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java
b/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java
new file mode 100644
index 000000000..a39dbba01
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/credential/CatalogCredentialContext.java
@@ -0,0 +1,38 @@
+/*
+ * 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.base.Preconditions;
+import javax.validation.constraints.NotNull;
+
+/** CatalogCredentialContext is generated when user requesting catalog
credentials. */
+public class CatalogCredentialContext implements CredentialContext {
+ @NotNull private final String userName;
+
+ public CatalogCredentialContext(String userName) {
+ Preconditions.checkNotNull(userName, "User name should not be null");
+ this.userName = userName;
+ }
+
+ @Override
+ public String getUserName() {
+ return userName;
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/credential/CredentialContext.java
b/core/src/main/java/org/apache/gravitino/credential/CredentialContext.java
new file mode 100644
index 000000000..6e82efea0
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/credential/CredentialContext.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+/** Contains credential context information to get credential from a
credential provider. */
+public interface CredentialContext {
+ /**
+ * Providing the username.
+ *
+ * @return A string identifying user name.
+ */
+ String getUserName();
+}
diff --git
a/core/src/main/java/org/apache/gravitino/credential/CredentialProvider.java
b/core/src/main/java/org/apache/gravitino/credential/CredentialProvider.java
new file mode 100644
index 000000000..4056cd00b
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/credential/CredentialProvider.java
@@ -0,0 +1,56 @@
+/*
+ * 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 java.io.Closeable;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/**
+ * Interface for credential providers.
+ *
+ * <p>A credential provider is responsible for managing and retrieving
credentials.
+ */
+public interface CredentialProvider extends Closeable {
+ /**
+ * Initializes the credential provider with catalog properties.
+ *
+ * @param properties catalog properties that can be used to configure the
provider. The specific
+ * properties required vary by implementation.
+ */
+ void initialize(Map<String, String> properties);
+
+ /**
+ * Returns the type of credential, it should be identical in Gravitino.
+ *
+ * @return A string identifying the type of credentials.
+ */
+ String credentialType();
+
+ /**
+ * Obtains a credential based on the provided context information.
+ *
+ * @param context A context object providing necessary information for
retrieving credentials.
+ * @return A Credential object containing the authentication information
needed to access a system
+ * or resource. Null will be returned if no credential is available.
+ */
+ @Nullable
+ Credential getCredential(CredentialContext context);
+}
diff --git
a/core/src/main/java/org/apache/gravitino/credential/CredentialProviderFactory.java
b/core/src/main/java/org/apache/gravitino/credential/CredentialProviderFactory.java
new file mode 100644
index 000000000..3833eeda9
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/credential/CredentialProviderFactory.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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CredentialProviderFactory {
+ private static final Logger LOG =
LoggerFactory.getLogger(CredentialProviderFactory.class);
+
+ public static CredentialProvider create(
+ String credentialType, Map<String, String> catalogProperties) {
+ Class<? extends CredentialProvider> providerClz =
lookupCredentialProvider(credentialType);
+ try {
+ CredentialProvider provider =
providerClz.getDeclaredConstructor().newInstance();
+ provider.initialize(catalogProperties);
+ return provider;
+ } catch (Exception e) {
+ LOG.warn("Create credential provider failed, {}", credentialType, e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class<? extends CredentialProvider> lookupCredentialProvider(
+ String credentialType) {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ ServiceLoader<CredentialProvider> serviceLoader =
+ ServiceLoader.load(CredentialProvider.class, classLoader);
+ List<Class<? extends CredentialProvider>> providers =
+ Streams.stream(serviceLoader.iterator())
+ .filter(
+ credentialProvider ->
+
credentialType.equalsIgnoreCase(credentialProvider.credentialType()))
+ .map(CredentialProvider::getClass)
+ .collect(Collectors.toList());
+
+ if (providers.isEmpty()) {
+ throw new IllegalArgumentException("No credential provider found for: "
+ credentialType);
+ } else if (providers.size() > 1) {
+ throw new IllegalArgumentException(
+ "Multiple credential providers found for: " + credentialType);
+ } else {
+ return Iterables.getOnlyElement(providers);
+ }
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java
b/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java
new file mode 100644
index 000000000..03e7bbe0e
--- /dev/null
+++
b/core/src/main/java/org/apache/gravitino/credential/PathBasedCredentialContext.java
@@ -0,0 +1,58 @@
+/*
+ * 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.base.Preconditions;
+import java.util.Set;
+import javax.validation.constraints.NotNull;
+
+/**
+ * LocationContext is generated when user requesting resources associated with
storage location like
+ * table, fileset, etc.
+ */
+public class PathBasedCredentialContext implements CredentialContext {
+
+ @NotNull private final Set<String> writePaths;
+ @NotNull private final Set<String> readPaths;
+ @NotNull private final String userName;
+
+ public PathBasedCredentialContext(
+ String userName, Set<String> writePaths, Set<String> readPaths) {
+ Preconditions.checkNotNull(userName, "User name should not be null");
+ Preconditions.checkNotNull(writePaths, "Write paths should not be null");
+ Preconditions.checkNotNull(readPaths, "Read paths should not be null");
+ this.userName = userName;
+ this.writePaths = writePaths;
+ this.readPaths = readPaths;
+ }
+
+ @Override
+ public String getUserName() {
+ return userName;
+ }
+
+ public Set<String> getWritePaths() {
+ return writePaths;
+ }
+
+ public Set<String> getReadPaths() {
+ return readPaths;
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java
b/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java
new file mode 100644
index 000000000..864635e96
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/credential/DummyCredentialProvider.java
@@ -0,0 +1,83 @@
+/*
+ * 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.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Set;
+import lombok.Getter;
+
+public class DummyCredentialProvider implements CredentialProvider {
+ Map<String, String> properties;
+ static final String CREDENTIAL_TYPE = "dummy";
+
+ @Override
+ public void initialize(Map<String, String> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public String credentialType() {
+ return CREDENTIAL_TYPE;
+ }
+
+ @Override
+ public Credential getCredential(CredentialContext context) {
+ Preconditions.checkArgument(
+ context instanceof PathBasedCredentialContext
+ || context instanceof CatalogCredentialContext,
+ "Doesn't support context: " + context.getClass().getSimpleName());
+ if (context instanceof PathBasedCredentialContext) {
+ return new DummyCredential((PathBasedCredentialContext) context);
+ }
+ return null;
+ }
+
+ public static class DummyCredential implements Credential {
+
+ @Getter private Set<String> writeLocations;
+ @Getter private Set<String> readLocations;
+
+ public DummyCredential(PathBasedCredentialContext locationContext) {
+ this.writeLocations = locationContext.getWritePaths();
+ this.readLocations = locationContext.getReadPaths();
+ }
+
+ @Override
+ public String credentialType() {
+ return DummyCredentialProvider.CREDENTIAL_TYPE;
+ }
+
+ @Override
+ public long expireTimeInMs() {
+ return 0;
+ }
+
+ @Override
+ public Map<String, String> credentialInfo() {
+ return ImmutableMap.of(
+ "writeLocation", writeLocations.toString(), "readLocation",
readLocations.toString());
+ }
+ }
+}
diff --git
a/core/src/test/java/org/apache/gravitino/credential/TestCredentialProvider.java
b/core/src/test/java/org/apache/gravitino/credential/TestCredentialProvider.java
new file mode 100644
index 000000000..b419375b1
--- /dev/null
+++
b/core/src/test/java/org/apache/gravitino/credential/TestCredentialProvider.java
@@ -0,0 +1,54 @@
+/*
+ * 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 java.util.Map;
+import org.apache.gravitino.credential.DummyCredentialProvider.DummyCredential;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
+
+public class TestCredentialProvider {
+ @Test
+ void testCredentialProvider() {
+ Map<String, String> catalogProperties = ImmutableMap.of("a", "b");
+ CredentialProvider credentialProvider =
+ CredentialProviderFactory.create(
+ DummyCredentialProvider.CREDENTIAL_TYPE, catalogProperties);
+ Assertions.assertEquals(
+ DummyCredentialProvider.CREDENTIAL_TYPE,
credentialProvider.credentialType());
+ Assertions.assertTrue(credentialProvider instanceof
DummyCredentialProvider);
+ DummyCredentialProvider dummyCredentialProvider =
(DummyCredentialProvider) credentialProvider;
+ Assertions.assertEquals(catalogProperties,
dummyCredentialProvider.properties);
+
+ ImmutableSet<String> writeLocations = ImmutableSet.of("location1");
+ ImmutableSet<String> readLocations = ImmutableSet.of("location2");
+
+ PathBasedCredentialContext locationContext =
+ new PathBasedCredentialContext("user", writeLocations, readLocations);
+ Credential credential =
dummyCredentialProvider.getCredential(locationContext);
+ Assertions.assertTrue(credential instanceof DummyCredential);
+ DummyCredential dummyCredential = (DummyCredential) credential;
+
+ Assertions.assertEquals(writeLocations,
dummyCredential.getWriteLocations());
+ Assertions.assertEquals(readLocations, dummyCredential.getReadLocations());
+ }
+}
diff --git
a/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
b/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
new file mode 100644
index 000000000..cbdbff0be
--- /dev/null
+++
b/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
@@ -0,0 +1,19 @@
+#
+# 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.DummyCredentialProvider
\ No newline at end of file