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 87b58fee3 [#3733] feat(core): Unified authorization framework (#3946)
87b58fee3 is described below
commit 87b58fee3ce630033857e9665ff0d63dd6d778c6
Author: Xun Liu <[email protected]>
AuthorDate: Wed Jul 10 17:20:14 2024 +0800
[#3733] feat(core): Unified authorization framework (#3946)
### What changes were proposed in this pull request?
Provide an authorization hook plugin framework, In the next step we can
develop an authorization plugin, just like Catalogs.
+ [Unified authorization design
document](https://docs.google.com/document/d/1RtKfU0uO-N7OjcrB3DOtY1ZsbhVp3GsLSJ26c_YQosQ/edit)
<img width="937" alt="image"
src="https://github.com/apache/gravitino/assets/3677382/b9a06b79-057a-494c-a1be-15691f478de1">
### Why are the changes needed?
Fix: #3733
### Does this PR introduce _any_ user-facing change?
N/A
### How was this patch tested?
CI Passed.
---
.../java/com/datastrato/gravitino/Catalog.java | 6 +
.../gravitino/authorization/RoleChange.java | 155 +++++++++++++++++++++
.../gravitino/authorization/SecurableObjects.java | 14 +-
build.gradle.kts | 3 +-
.../catalog/hive/TestHiveCatalogOperations.java | 3 +-
.../gravitino/authorization/PermissionManager.java | 2 +-
.../gravitino/connector/BaseCatalog.java | 63 +++++++++
.../connector/BaseCatalogPropertiesMetadata.java | 7 +
.../authorization/AuthorizationPlugin.java | 29 ++++
.../authorization/AuthorizationProvider.java | 33 +++++
.../connector/authorization/BaseAuthorization.java | 64 +++++++++
.../authorization/RoleAuthorizationPlugin.java | 70 ++++++++++
.../UserGroupAuthorizationPlugin.java | 143 +++++++++++++++++++
.../com/datastrato/gravitino/meta/RoleEntity.java | 2 +-
.../java/com/datastrato/gravitino/TestCatalog.java | 9 ++
.../connector/authorization/TestAuthorization.java | 95 +++++++++++++
.../mysql/TestMySQLAuthorization.java | 37 +++++
.../mysql/TestMySQLAuthorizationPlugin.java | 105 ++++++++++++++
.../ranger/TestRangerAuthorization.java | 37 +++++
.../ranger/TestRangerAuthorizationPlugin.java | 105 ++++++++++++++
...o.connector.authorization.AuthorizationProvider | 20 +++
integration-test/build.gradle.kts | 2 +-
.../test/authorization/ranger/RangerIT.java | 56 +++++++-
23 files changed, 1042 insertions(+), 18 deletions(-)
diff --git a/api/src/main/java/com/datastrato/gravitino/Catalog.java
b/api/src/main/java/com/datastrato/gravitino/Catalog.java
index 2f75cab38..d7627cf14 100644
--- a/api/src/main/java/com/datastrato/gravitino/Catalog.java
+++ b/api/src/main/java/com/datastrato/gravitino/Catalog.java
@@ -88,6 +88,12 @@ public interface Catalog extends Auditable {
*/
String CLOUD_REGION_CODE = "cloud.region-code";
+ /**
+ * This variable is used as a key in properties of catalogs to use
authorization provider in
+ * Gravitino.
+ */
+ String AUTHORIZATION_PROVIDER = "authorization-provider";
+
/** @return The name of the catalog. */
String name();
diff --git
a/api/src/main/java/com/datastrato/gravitino/authorization/RoleChange.java
b/api/src/main/java/com/datastrato/gravitino/authorization/RoleChange.java
new file mode 100644
index 000000000..4271bc7f0
--- /dev/null
+++ b/api/src/main/java/com/datastrato/gravitino/authorization/RoleChange.java
@@ -0,0 +1,155 @@
+/*
+ * 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 com.datastrato.gravitino.authorization;
+
+import com.datastrato.gravitino.annotation.Evolving;
+
+/** The RoleChange interface defines the public API for managing roles in an
authorization. */
+@Evolving
+public interface RoleChange {
+ /**
+ * Create a RoleChange to add a securable object into a role.
+ *
+ * @param securableObject The securable object.
+ * @return return a RoleChange for the add securable object.
+ */
+ static RoleChange addSecurableObject(SecurableObject securableObject) {
+ return new AddSecurableObject(securableObject);
+ }
+
+ /**
+ * Create a RoleChange to remove a securable object from a role.
+ *
+ * @param securableObject The securable object.
+ * @return return a RoleChange for the add securable object.
+ */
+ static RoleChange removeSecurableObject(SecurableObject securableObject) {
+ return new RemoveSecurableObject(securableObject);
+ }
+
+ /** A AddSecurableObject to add securable object to role. */
+ final class AddSecurableObject implements RoleChange {
+ private final SecurableObject securableObject;
+
+ private AddSecurableObject(SecurableObject securableObject) {
+ this.securableObject = securableObject;
+ }
+
+ /**
+ * Returns the securable object to be added.
+ *
+ * @return return a securable object.
+ */
+ public SecurableObject getSecurableObject() {
+ return this.securableObject;
+ }
+
+ /**
+ * Compares this AddSecurableObject instance with another object for
equality. The comparison is
+ * based on the add securable object to role.
+ *
+ * @param o The object to compare with this instance.
+ * @return true if the given object represents the same add securable
object; false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AddSecurableObject that = (AddSecurableObject) o;
+ return securableObject.equals(that.securableObject);
+ }
+
+ /**
+ * Generates a hash code for this AddSecurableObject instance. The hash
code is based on the add
+ * securable object.
+ *
+ * @return A hash code value for this add securable object operation.
+ */
+ @Override
+ public int hashCode() {
+ return securableObject.hashCode();
+ }
+
+ /**
+ * Returns a string representation of the AddSecurableObject instance.
This string format
+ * includes the class name followed by the add securable object operation.
+ *
+ * @return A string representation of the AddSecurableObject instance.
+ */
+ @Override
+ public String toString() {
+ return "ADDSECURABLEOBJECT " + securableObject;
+ }
+ }
+
+ /** A RemoveSecurableObject to remove securable object from role. */
+ final class RemoveSecurableObject implements RoleChange {
+ private final SecurableObject securableObject;
+
+ private RemoveSecurableObject(SecurableObject securableObject) {
+ this.securableObject = securableObject;
+ }
+
+ /**
+ * Returns the securable object to be added.
+ *
+ * @return return a securable object.
+ */
+ public SecurableObject getSecurableObject() {
+ return this.securableObject;
+ }
+
+ /**
+ * Compares this RemoveSecurableObject instance with another object for
equality. The comparison
+ * is based on the add securable object to role.
+ *
+ * @param o The object to compare with this instance.
+ * @return true if the given object represents the same add securable
object; false otherwise.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RemoveSecurableObject that = (RemoveSecurableObject) o;
+ return securableObject.equals(that.securableObject);
+ }
+
+ /**
+ * Generates a hash code for this RemoveSecurableObject instance. The hash
code is based on the
+ * add securable object.
+ *
+ * @return A hash code value for this add securable object operation.
+ */
+ @Override
+ public int hashCode() {
+ return securableObject.hashCode();
+ }
+
+ /**
+ * Returns a string representation of the RemoveSecurableObject instance.
This string format
+ * includes the class name followed by the add securable object operation.
+ *
+ * @return A string representation of the RemoveSecurableObject instance.
+ */
+ @Override
+ public String toString() {
+ return "REMOVESECURABLEOBJECT " + securableObject;
+ }
+ }
+}
diff --git
a/api/src/main/java/com/datastrato/gravitino/authorization/SecurableObjects.java
b/api/src/main/java/com/datastrato/gravitino/authorization/SecurableObjects.java
index fa1ded6ea..1037f06a7 100644
---
a/api/src/main/java/com/datastrato/gravitino/authorization/SecurableObjects.java
+++
b/api/src/main/java/com/datastrato/gravitino/authorization/SecurableObjects.java
@@ -40,7 +40,7 @@ public class SecurableObjects {
* @return The created metalake {@link SecurableObject}
*/
public static SecurableObject ofMetalake(String metalake, List<Privilege>
privileges) {
- return of(SecurableObject.Type.METALAKE, Lists.newArrayList(metalake),
privileges);
+ return of(MetadataObject.Type.METALAKE, Lists.newArrayList(metalake),
privileges);
}
/**
@@ -51,7 +51,7 @@ public class SecurableObjects {
* @return The created catalog {@link SecurableObject}
*/
public static SecurableObject ofCatalog(String catalog, List<Privilege>
privileges) {
- return of(SecurableObject.Type.CATALOG, Lists.newArrayList(catalog),
privileges);
+ return of(MetadataObject.Type.CATALOG, Lists.newArrayList(catalog),
privileges);
}
/**
@@ -66,7 +66,7 @@ public class SecurableObjects {
public static SecurableObject ofSchema(
SecurableObject catalog, String schema, List<Privilege> privileges) {
return of(
- SecurableObject.Type.SCHEMA, Lists.newArrayList(catalog.fullName(),
schema), privileges);
+ MetadataObject.Type.SCHEMA, Lists.newArrayList(catalog.fullName(),
schema), privileges);
}
/**
@@ -81,7 +81,7 @@ public class SecurableObjects {
SecurableObject schema, String table, List<Privilege> privileges) {
List<String> names =
Lists.newArrayList(DOT_SPLITTER.splitToList(schema.fullName()));
names.add(table);
- return of(SecurableObject.Type.TABLE, names, privileges);
+ return of(MetadataObject.Type.TABLE, names, privileges);
}
/**
@@ -96,7 +96,7 @@ public class SecurableObjects {
SecurableObject schema, String topic, List<Privilege> privileges) {
List<String> names =
Lists.newArrayList(DOT_SPLITTER.splitToList(schema.fullName()));
names.add(topic);
- return of(SecurableObject.Type.TOPIC, names, privileges);
+ return of(MetadataObject.Type.TOPIC, names, privileges);
}
/**
@@ -112,7 +112,7 @@ public class SecurableObjects {
SecurableObject schema, String fileset, List<Privilege> privileges) {
List<String> names =
Lists.newArrayList(DOT_SPLITTER.splitToList(schema.fullName()));
names.add(fileset);
- return of(SecurableObject.Type.FILESET, names, privileges);
+ return of(MetadataObject.Type.FILESET, names, privileges);
}
/**
@@ -125,7 +125,7 @@ public class SecurableObjects {
* @return The created {@link SecurableObject}
*/
public static SecurableObject ofAllMetalakes(List<Privilege> privileges) {
- return new SecurableObjectImpl(null, "*", SecurableObject.Type.METALAKE,
privileges);
+ return new SecurableObjectImpl(null, "*", MetadataObject.Type.METALAKE,
privileges);
}
private static class SecurableObjectImpl extends
MetadataObjects.MetadataObjectImpl
diff --git a/build.gradle.kts b/build.gradle.kts
index e8d2b477e..1508a0b32 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -487,6 +487,7 @@ tasks.rat {
"DISCLAIMER.txt",
"ROADMAP.md",
"clients/client-python/.pytest_cache/*",
+ "clients/client-python/.venv/*",
"clients/client-python/gravitino.egg-info/*",
"clients/client-python/gravitino/utils/exceptions.py",
"clients/client-python/gravitino/utils/http_client.py"
@@ -709,7 +710,7 @@ fun printDockerCheckInfo() {
if (dockerTest) {
println("Using Docker container to run all tests. [$testMode test]")
} else {
- println("Run test cases without `gravitino-docker-test` tag
................ [$testMode test]")
+ println("Run test cases without `gravitino-docker-test` tag ..............
[$testMode test]")
}
println("-----------------------------------------------------------------")
diff --git
a/catalogs/catalog-hive/src/test/java/com/datastrato/gravitino/catalog/hive/TestHiveCatalogOperations.java
b/catalogs/catalog-hive/src/test/java/com/datastrato/gravitino/catalog/hive/TestHiveCatalogOperations.java
index edf23bab8..4e6e8d3d3 100644
---
a/catalogs/catalog-hive/src/test/java/com/datastrato/gravitino/catalog/hive/TestHiveCatalogOperations.java
+++
b/catalogs/catalog-hive/src/test/java/com/datastrato/gravitino/catalog/hive/TestHiveCatalogOperations.java
@@ -84,10 +84,11 @@ class TestHiveCatalogOperations {
Map<String, PropertyEntry<?>> propertyEntryMap =
HIVE_PROPERTIES_METADATA.catalogPropertiesMetadata().propertyEntries();
- Assertions.assertEquals(14, propertyEntryMap.size());
+ Assertions.assertEquals(15, propertyEntryMap.size());
Assertions.assertTrue(propertyEntryMap.containsKey(METASTORE_URIS));
Assertions.assertTrue(propertyEntryMap.containsKey(Catalog.PROPERTY_PACKAGE));
Assertions.assertTrue(propertyEntryMap.containsKey(BaseCatalog.CATALOG_OPERATION_IMPL));
+
Assertions.assertTrue(propertyEntryMap.containsKey(BaseCatalog.AUTHORIZATION_PROVIDER));
Assertions.assertTrue(propertyEntryMap.containsKey(CLIENT_POOL_SIZE));
Assertions.assertTrue(propertyEntryMap.containsKey(IMPERSONATION_ENABLE));
Assertions.assertTrue(propertyEntryMap.containsKey(LIST_ALL_TABLES));
diff --git
a/core/src/main/java/com/datastrato/gravitino/authorization/PermissionManager.java
b/core/src/main/java/com/datastrato/gravitino/authorization/PermissionManager.java
index f30f41877..3b24e8cde 100644
---
a/core/src/main/java/com/datastrato/gravitino/authorization/PermissionManager.java
+++
b/core/src/main/java/com/datastrato/gravitino/authorization/PermissionManager.java
@@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
/**
* PermissionManager is used for managing the logic the granting and revoking
roles. Role is used
- * for manging permissions. GrantManager will filter the invalid roles, too.
+ * for manging permissions. PermissionManager will filter the invalid roles,
too.
*/
class PermissionManager {
private static final Logger LOG =
LoggerFactory.getLogger(PermissionManager.class);
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalog.java
b/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalog.java
index bdf79827b..a353f84fe 100644
--- a/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalog.java
+++ b/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalog.java
@@ -22,15 +22,23 @@ import com.datastrato.gravitino.Audit;
import com.datastrato.gravitino.Catalog;
import com.datastrato.gravitino.CatalogProvider;
import com.datastrato.gravitino.annotation.Evolving;
+import com.datastrato.gravitino.connector.authorization.AuthorizationPlugin;
+import com.datastrato.gravitino.connector.authorization.AuthorizationProvider;
+import com.datastrato.gravitino.connector.authorization.BaseAuthorization;
import com.datastrato.gravitino.connector.capability.Capability;
import com.datastrato.gravitino.meta.CatalogEntity;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
+import com.google.common.collect.Streams;
import java.io.Closeable;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,6 +65,9 @@ public abstract class BaseCatalog<T extends BaseCatalog>
// The object you used is not stable, don't use it unless you know what you
are doing.
@VisibleForTesting public static final String CATALOG_OPERATION_IMPL =
"ops-impl";
+ // Underlying access control system plugin for this catalog.
+ private volatile BaseAuthorization<?> authorization;
+
private CatalogEntity entity;
private Map<String, String> conf;
@@ -169,12 +180,64 @@ public abstract class BaseCatalog<T extends BaseCatalog>
return ops;
}
+ public AuthorizationPlugin getAuthorizationPlugin() {
+ if (authorization == null) {
+ synchronized (this) {
+ if (authorization == null) {
+ BaseAuthorization<?> baseAuthorization =
createAuthorizationPluginInstance();
+ if (baseAuthorization == null) {
+ return null;
+ }
+ authorization = baseAuthorization;
+ }
+ }
+ }
+ return authorization.plugin();
+ }
+
+ private BaseAuthorization<?> createAuthorizationPluginInstance() {
+ String authorizationProvider =
+ (String) catalogPropertiesMetadata().getOrDefault(conf,
AUTHORIZATION_PROVIDER);
+ if (authorizationProvider == null) {
+ LOG.info("Authorization provider is not set!");
+ return null;
+ }
+
+ ServiceLoader<AuthorizationProvider> loader =
+ ServiceLoader.load(
+ AuthorizationProvider.class,
Thread.currentThread().getContextClassLoader());
+
+ List<Class<? extends AuthorizationProvider>> providers =
+ Streams.stream(loader.iterator())
+ .filter(p -> p.shortName().equalsIgnoreCase(authorizationProvider))
+ .map(AuthorizationProvider::getClass)
+ .collect(Collectors.toList());
+ if (providers.isEmpty()) {
+ throw new IllegalArgumentException(
+ "No authorization provider found for: " + authorizationProvider);
+ } else if (providers.size() > 1) {
+ throw new IllegalArgumentException(
+ "Multiple authorization providers found for: " +
authorizationProvider);
+ }
+ try {
+ return (BaseAuthorization<?>)
+
Iterables.getOnlyElement(providers).getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ LOG.error("Failed to create authorization instance", e);
+ throw new RuntimeException(e);
+ }
+ }
+
@Override
public void close() throws IOException {
if (ops != null) {
ops.close();
ops = null;
}
+ if (authorization != null) {
+ authorization.close();
+ authorization = null;
+ }
}
public Capability capability() {
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalogPropertiesMetadata.java
b/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalogPropertiesMetadata.java
index 2eada4e92..d2f115a66 100644
---
a/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalogPropertiesMetadata.java
+++
b/core/src/main/java/com/datastrato/gravitino/connector/BaseCatalogPropertiesMetadata.java
@@ -48,6 +48,13 @@ public abstract class BaseCatalogPropertiesMetadata extends
BasePropertiesMetada
null,
false,
false),
+ PropertyEntry.stringImmutablePropertyEntry(
+ Catalog.AUTHORIZATION_PROVIDER,
+ "The name of the authorization provider for Gravitino",
+ false,
+ null,
+ false,
+ false),
PropertyEntry.enumPropertyEntry(
CLOUD_NAME,
"The cloud that the catalog is running on",
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationPlugin.java
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationPlugin.java
new file mode 100644
index 000000000..4e5fe70cb
--- /dev/null
+++
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationPlugin.java
@@ -0,0 +1,29 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+import java.io.Closeable;
+
+/**
+ * Authorization operations plugin interfaces. <br>
+ * Notice: Because each interface function needs to perform multiple steps in
the underlying
+ * permission system, the implementation method of these function interface
must be idempotent.
+ */
+public interface AuthorizationPlugin
+ extends UserGroupAuthorizationPlugin, RoleAuthorizationPlugin, Closeable {}
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationProvider.java
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationProvider.java
new file mode 100644
index 000000000..efc7a4948
--- /dev/null
+++
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/AuthorizationProvider.java
@@ -0,0 +1,33 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+/**
+ * An Authorization provider is a class that provides a short name for an
authorization. <br>
+ * This short name is used when creating an authorization.
+ */
+public interface AuthorizationProvider {
+ /**
+ * The string that represents the authorization that this provider uses. <br>
+ * This is overridden by children to provide a nice alias for the
authorization.
+ *
+ * @return The string that represents the authorization that this provider
uses.
+ */
+ String shortName();
+}
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/authorization/BaseAuthorization.java
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/BaseAuthorization.java
new file mode 100644
index 000000000..9855c2f54
--- /dev/null
+++
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/BaseAuthorization.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * The abstract base class for Authorization implementations.<br>
+ * A typical authorization always contain {@link AuthorizationPlugin} which is
used to trigger the
+ * specific operations by the authorization. <br>
+ * For example, a Ranger authorization has a RangerAuthorizationPlugin which
manipulates Ranger to
+ * management Hive and HDFS permission. <br>
+ *
+ * @param <T> The type of the concrete subclass of BaseAuthorization.
+ */
+public abstract class BaseAuthorization<T extends BaseAuthorization>
+ implements AuthorizationProvider, Closeable {
+ private volatile AuthorizationPlugin plugin = null;
+
+ /**
+ * Creates a new instance of AuthorizationPlugin. <br>
+ * The child class should implement this method to provide a specific
AuthorizationPlugin instance
+ * regarding that authorization. <br>
+ *
+ * @return A new instance of AuthorizationHook.
+ */
+ protected abstract AuthorizationPlugin newPlugin();
+
+ public AuthorizationPlugin plugin() {
+ if (plugin == null) {
+ synchronized (this) {
+ if (plugin == null) {
+ plugin = newPlugin();
+ }
+ }
+ }
+
+ return plugin;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (plugin != null) {
+ plugin.close();
+ }
+ }
+}
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/authorization/RoleAuthorizationPlugin.java
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/RoleAuthorizationPlugin.java
new file mode 100644
index 000000000..6095cc796
--- /dev/null
+++
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/RoleAuthorizationPlugin.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+import com.datastrato.gravitino.authorization.Role;
+import com.datastrato.gravitino.authorization.RoleChange;
+
+/** Interface for authorization Role plugin operation of the underlying access
control system */
+interface RoleAuthorizationPlugin {
+ /**
+ * After creating a role in Gravitino, this method is called to create the
role in the underlying
+ * system.<br>
+ *
+ * @param role The entity of the Role.
+ * @return True if the create operation success; False if the create
operation failed.
+ * @throws RuntimeException If creating the Role encounters storage issues.
+ */
+ Boolean onRoleCreated(Role role) throws RuntimeException;
+
+ /**
+ * After acquiring a role from Gravitino, this method is called to acquire
the role in the
+ * underlying system.<br>
+ * Because role information is already stored in the Gravitino, so we don't
need to get the Role
+ * from the underlying access control system. <br>
+ * We only need to check if the Role exists in the underlying access control
system.
+ *
+ * @param role The entity of the Role.
+ * @return IF exist return true, else return false.
+ * @throws RuntimeException If getting the Role encounters underlying access
control system
+ * issues.
+ */
+ Boolean onRoleAcquired(Role role) throws RuntimeException;
+
+ /**
+ * After deleting a role from Gravitino, this method is called to delete the
role in the
+ * underlying system. <br>
+ *
+ * @param role The entity of the Role.
+ * @return True if the Role was successfully deleted, false only when
there's no such role
+ * @throws RuntimeException If deleting the Role encounters storage issues.
+ */
+ Boolean onRoleDeleted(Role role) throws RuntimeException;
+
+ /**
+ * After updating a role in Gravitino, this method is called to update the
role in the underlying
+ * system. <br>
+ *
+ * @param role The entity of the Role.
+ * @param changes role changes apply to the role.
+ * @return True if the update operation is successful; False if the update
operation fails.
+ * @throws RuntimeException If update role encounters storage issues.
+ */
+ Boolean onRoleUpdated(Role role, RoleChange... changes) throws
RuntimeException;
+}
diff --git
a/core/src/main/java/com/datastrato/gravitino/connector/authorization/UserGroupAuthorizationPlugin.java
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/UserGroupAuthorizationPlugin.java
new file mode 100644
index 000000000..e4f1dc81d
--- /dev/null
+++
b/core/src/main/java/com/datastrato/gravitino/connector/authorization/UserGroupAuthorizationPlugin.java
@@ -0,0 +1,143 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+import com.datastrato.gravitino.authorization.Group;
+import com.datastrato.gravitino.authorization.Role;
+import com.datastrato.gravitino.authorization.User;
+import java.util.List;
+
+/**
+ * Interface for authorization User and Group plugin operation of the
underlying access control
+ * system.
+ */
+interface UserGroupAuthorizationPlugin {
+ /**
+ * After adding a User to Gravitino, this method is called to add the User
to the underlying
+ * system. <br>
+ *
+ * @param user The user entity.
+ * @return True if the add User was successfully added, false if the add
User failed.
+ * @throws RuntimeException If adding the User encounters storage issues.
+ */
+ Boolean onUserAdded(User user) throws RuntimeException;
+
+ /**
+ * After removing a User from Gravitino, this method is called to remove the
User from the
+ * underlying system. <br>
+ *
+ * @param user The user entity.
+ * @return True if the User was successfully removed, false if the remove
User failed.
+ * @throws RuntimeException If removing the User encounters storage issues.
+ */
+ Boolean onUserRemoved(User user) throws RuntimeException;
+
+ /**
+ * After acquiring a User from Gravitino, this method is called to acquire
the User in the
+ * underlying system. <br>
+ * Because User information is already stored in the Gravitino, so we don't
need to get the User
+ * from the underlying access control system. <br>
+ * We only need to check if the User exists in the underlying access control
system.
+ *
+ * @param user The user entity.
+ * @return IF exist return true, else return false.
+ * @throws RuntimeException If getting the User encounters underlying access
control system
+ * issues.
+ */
+ Boolean onUserAcquired(User user) throws RuntimeException;
+
+ /**
+ * After adding a Group to Gravitino, this method is called to add the Group
to the underlying
+ * system. <br>
+ *
+ * @param group The group entity.
+ * @return True if the add Group was successfully added, false if the add
Group failed.
+ * @throws RuntimeException If adding the Group encounters storage issues.
+ */
+ Boolean onGroupAdded(Group group) throws RuntimeException;
+
+ /**
+ * After removing a Group from Gravitino, this method is called to remove
the Group from the
+ * underlying system. <br>
+ *
+ * @param group The group entity.
+ * @return True if the remove Group was successfully removed, false if the
remove Group was
+ * failed.
+ * @throws RuntimeException If removing the Group encounters storage issues.
+ */
+ Boolean onGroupRemoved(Group group) throws RuntimeException;
+
+ /**
+ * After acquiring a Group from Gravitino, this method is called to acquire
the Group in the
+ * underlying system. <br>
+ * Because Group information is already stored in the Gravitino, so we don't
need to get the Group
+ * from the underlying access control system. <br>
+ * We only need to check if the Group exists in the underlying access
control system. <br>
+ *
+ * @param group The group entity.
+ * @return If exist return true, else return false.
+ * @throws RuntimeException If getting the Group encounters underlying
access control system
+ * issues.
+ */
+ Boolean onGroupAcquired(Group group) throws RuntimeException;
+
+ /**
+ * After granting roles to a user from Gravitino, this method is called to
grant roles to the user
+ * in the underlying system. <br>
+ *
+ * @param user The entity of the User.
+ * @param roles The entities of the Roles.
+ * @return True if the Grant was successful, false if the Grant was failed.
+ * @throws RuntimeException If granting roles to a user encounters storage
issues.
+ */
+ Boolean onGrantedRolesToUser(List<Role> roles, User user) throws
RuntimeException;
+
+ /**
+ * After revoking roles from a user from Gravitino, this method is called to
revoke roles from the
+ * user in the underlying system. <br>
+ *
+ * @param user The entity of the User.
+ * @param roles The entities of the Roles.
+ * @return True if the revoke was successfully removed, false if the revoke
failed.
+ * @throws RuntimeException If revoking roles from a user encounters storage
issues.
+ */
+ Boolean onRevokedRolesFromUser(List<Role> roles, User user) throws
RuntimeException;
+
+ /**
+ * After granting roles to a group from Gravitino, this method is called to
grant roles to the
+ * group in the underlying system. <br>
+ *
+ * @param group The entity of the Group.
+ * @param roles The entities of the Roles.
+ * @return True if the revoke was successfully removed, False if the revoke
failed.
+ * @throws RuntimeException If granting roles to a group encounters storage
issues.
+ */
+ Boolean onGrantedRolesToGroup(List<Role> roles, Group group) throws
RuntimeException;
+
+ /**
+ * After revoking roles from a group from Gravitino, this method is called
to revoke roles from
+ * the group in the underlying system. <br>
+ *
+ * @param group The entity of the Group.
+ * @param roles The entities of the Roles.
+ * @return True if the revoke was successfully removed, False if the revoke
failed.
+ * @throws RuntimeException If revoking roles from a group encounters
storage issues.
+ */
+ Boolean onRevokedRolesFromGroup(List<Role> roles, Group group) throws
RuntimeException;
+}
diff --git a/core/src/main/java/com/datastrato/gravitino/meta/RoleEntity.java
b/core/src/main/java/com/datastrato/gravitino/meta/RoleEntity.java
index e1101ce9b..2c2c4a76b 100644
--- a/core/src/main/java/com/datastrato/gravitino/meta/RoleEntity.java
+++ b/core/src/main/java/com/datastrato/gravitino/meta/RoleEntity.java
@@ -156,7 +156,7 @@ public class RoleEntity implements Role, Entity, Auditable,
HasIdentifier {
@Override
public int hashCode() {
- return Objects.hash(id, name, properties, auditInfo, securableObjects);
+ return Objects.hash(id, name, properties, auditInfo, securableObjects,
namespace);
}
/**
diff --git a/core/src/test/java/com/datastrato/gravitino/TestCatalog.java
b/core/src/test/java/com/datastrato/gravitino/TestCatalog.java
index 8d4668865..a732ab0b8 100644
--- a/core/src/test/java/com/datastrato/gravitino/TestCatalog.java
+++ b/core/src/test/java/com/datastrato/gravitino/TestCatalog.java
@@ -115,6 +115,15 @@ public class TestCatalog extends BaseCatalog<TestCatalog> {
false,
false,
false))
+ .put(
+ AUTHORIZATION_PROVIDER,
+ PropertyEntry.stringImmutablePropertyEntry(
+ Catalog.AUTHORIZATION_PROVIDER,
+ "The name of the authorization provider for Gravitino",
+ false,
+ null,
+ false,
+ false))
.build();
}
};
diff --git
a/core/src/test/java/com/datastrato/gravitino/connector/authorization/TestAuthorization.java
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/TestAuthorization.java
new file mode 100644
index 000000000..58b25fe20
--- /dev/null
+++
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/TestAuthorization.java
@@ -0,0 +1,95 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization;
+
+import com.datastrato.gravitino.Catalog;
+import com.datastrato.gravitino.Namespace;
+import com.datastrato.gravitino.TestCatalog;
+import
com.datastrato.gravitino.connector.authorization.mysql.TestMySQLAuthorizationPlugin;
+import
com.datastrato.gravitino.connector.authorization.ranger.TestRangerAuthorizationPlugin;
+import com.datastrato.gravitino.meta.AuditInfo;
+import com.datastrato.gravitino.meta.CatalogEntity;
+import com.google.common.collect.ImmutableMap;
+import java.time.Instant;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class TestAuthorization {
+ private static TestCatalog hiveCatalog;
+ private static TestCatalog mySQLCatalog;
+
+ @BeforeAll
+ public static void setUp() throws Exception {
+ AuditInfo auditInfo =
+
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build();
+
+ CatalogEntity hiveCatalogEntity =
+ CatalogEntity.builder()
+ .withId(1L)
+ .withName("catalog-test1")
+ .withNamespace(Namespace.of("default"))
+ .withType(Catalog.Type.RELATIONAL)
+ .withProvider("test")
+ .withAuditInfo(auditInfo)
+ .build();
+
+ hiveCatalog =
+ new TestCatalog()
+ .withCatalogConf(ImmutableMap.of(Catalog.AUTHORIZATION_PROVIDER,
"ranger"))
+ .withCatalogEntity(hiveCatalogEntity);
+
+ CatalogEntity mySQLEntity =
+ CatalogEntity.builder()
+ .withId(2L)
+ .withName("catalog-test2")
+ .withNamespace(Namespace.of("default"))
+ .withType(Catalog.Type.RELATIONAL)
+ .withProvider("test")
+ .withAuditInfo(auditInfo)
+ .build();
+
+ mySQLCatalog =
+ new TestCatalog()
+ .withCatalogConf(ImmutableMap.of(Catalog.AUTHORIZATION_PROVIDER,
"mysql"))
+ .withCatalogEntity(mySQLEntity);
+ }
+
+ @Test
+ public void testRangerAuthorization() {
+ AuthorizationPlugin rangerAuthPlugin =
hiveCatalog.getAuthorizationPlugin();
+ Assertions.assertInstanceOf(TestRangerAuthorizationPlugin.class,
rangerAuthPlugin);
+ TestRangerAuthorizationPlugin testRangerAuthPlugin =
+ (TestRangerAuthorizationPlugin) rangerAuthPlugin;
+ Assertions.assertFalse(testRangerAuthPlugin.callOnCreateRole1);
+ rangerAuthPlugin.onRoleCreated(null);
+ Assertions.assertTrue(testRangerAuthPlugin.callOnCreateRole1);
+ }
+
+ @Test
+ public void testMySQLAuthorization() {
+ AuthorizationPlugin mySQLAuthPlugin =
mySQLCatalog.getAuthorizationPlugin();
+ Assertions.assertInstanceOf(TestMySQLAuthorizationPlugin.class,
mySQLAuthPlugin);
+ TestMySQLAuthorizationPlugin testMySQLAuthPlugin =
+ (TestMySQLAuthorizationPlugin) mySQLAuthPlugin;
+ Assertions.assertFalse(testMySQLAuthPlugin.callOnCreateRole2);
+ mySQLAuthPlugin.onRoleCreated(null);
+ Assertions.assertTrue(testMySQLAuthPlugin.callOnCreateRole2);
+ }
+}
diff --git
a/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorization.java
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorization.java
new file mode 100644
index 000000000..08eb787fb
--- /dev/null
+++
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorization.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization.mysql;
+
+import com.datastrato.gravitino.connector.authorization.AuthorizationPlugin;
+import com.datastrato.gravitino.connector.authorization.BaseAuthorization;
+
+public class TestMySQLAuthorization extends
BaseAuthorization<TestMySQLAuthorization> {
+
+ public TestMySQLAuthorization() {}
+
+ @Override
+ public String shortName() {
+ return "mysql";
+ }
+
+ @Override
+ protected AuthorizationPlugin newPlugin() {
+ return new TestMySQLAuthorizationPlugin();
+ }
+}
diff --git
a/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorizationPlugin.java
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorizationPlugin.java
new file mode 100644
index 000000000..1ab26526d
--- /dev/null
+++
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/mysql/TestMySQLAuthorizationPlugin.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization.mysql;
+
+import com.datastrato.gravitino.authorization.Group;
+import com.datastrato.gravitino.authorization.Role;
+import com.datastrato.gravitino.authorization.RoleChange;
+import com.datastrato.gravitino.authorization.User;
+import com.datastrato.gravitino.connector.authorization.AuthorizationPlugin;
+import java.io.IOException;
+import java.util.List;
+
+public class TestMySQLAuthorizationPlugin implements AuthorizationPlugin {
+ public boolean callOnCreateRole2 = false;
+
+ @Override
+ public Boolean onRoleCreated(Role role) throws RuntimeException {
+ callOnCreateRole2 = true;
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleAcquired(Role role) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleDeleted(Role role) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleUpdated(Role role, RoleChange... changes) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserAdded(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserRemoved(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserAcquired(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupAdded(Group group) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupRemoved(Group group) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupAcquired(Group group) {
+ return null;
+ }
+
+ @Override
+ public Boolean onGrantedRolesToUser(List<Role> roles, User user) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRevokedRolesFromUser(List<Role> roles, User user) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGrantedRolesToGroup(List<Role> roles, Group group) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRevokedRolesFromGroup(List<Role> roles, Group group) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public void close() throws IOException {}
+}
diff --git
a/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorization.java
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorization.java
new file mode 100644
index 000000000..0f4cf4d85
--- /dev/null
+++
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorization.java
@@ -0,0 +1,37 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization.ranger;
+
+import com.datastrato.gravitino.connector.authorization.AuthorizationPlugin;
+import com.datastrato.gravitino.connector.authorization.BaseAuthorization;
+
+public class TestRangerAuthorization extends
BaseAuthorization<TestRangerAuthorization> {
+
+ public TestRangerAuthorization() {}
+
+ @Override
+ public String shortName() {
+ return "ranger";
+ }
+
+ @Override
+ protected AuthorizationPlugin newPlugin() {
+ return new TestRangerAuthorizationPlugin();
+ }
+}
diff --git
a/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorizationPlugin.java
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorizationPlugin.java
new file mode 100644
index 000000000..cf0f29ec2
--- /dev/null
+++
b/core/src/test/java/com/datastrato/gravitino/connector/authorization/ranger/TestRangerAuthorizationPlugin.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.datastrato.gravitino.connector.authorization.ranger;
+
+import com.datastrato.gravitino.authorization.Group;
+import com.datastrato.gravitino.authorization.Role;
+import com.datastrato.gravitino.authorization.RoleChange;
+import com.datastrato.gravitino.authorization.User;
+import com.datastrato.gravitino.connector.authorization.AuthorizationPlugin;
+import java.io.IOException;
+import java.util.List;
+
+public class TestRangerAuthorizationPlugin implements AuthorizationPlugin {
+ public boolean callOnCreateRole1 = false;
+
+ @Override
+ public Boolean onRoleCreated(Role role) throws RuntimeException {
+ callOnCreateRole1 = true;
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleAcquired(Role role) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleDeleted(Role role) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRoleUpdated(Role role, RoleChange... changes) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserAdded(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserRemoved(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onUserAcquired(User user) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupAdded(Group group) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupRemoved(Group group) throws RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGroupAcquired(Group group) {
+ return null;
+ }
+
+ @Override
+ public Boolean onGrantedRolesToUser(List<Role> roles, User user) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRevokedRolesFromUser(List<Role> roles, User user) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onGrantedRolesToGroup(List<Role> roles, Group group) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public Boolean onRevokedRolesFromGroup(List<Role> roles, Group group) throws
RuntimeException {
+ return null;
+ }
+
+ @Override
+ public void close() throws IOException {}
+}
diff --git
a/core/src/test/resources/META-INF/services/com.datastrato.gravitino.connector.authorization.AuthorizationProvider
b/core/src/test/resources/META-INF/services/com.datastrato.gravitino.connector.authorization.AuthorizationProvider
new file mode 100644
index 000000000..e2f90e557
--- /dev/null
+++
b/core/src/test/resources/META-INF/services/com.datastrato.gravitino.connector.authorization.AuthorizationProvider
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+com.datastrato.gravitino.connector.authorization.ranger.TestRangerAuthorization
+com.datastrato.gravitino.connector.authorization.mysql.TestMySQLAuthorization
\ No newline at end of file
diff --git a/integration-test/build.gradle.kts
b/integration-test/build.gradle.kts
index 016142e0b..e51f72799 100644
--- a/integration-test/build.gradle.kts
+++ b/integration-test/build.gradle.kts
@@ -161,7 +161,7 @@ tasks.test {
environment("GRAVITINO_CI_TRINO_DOCKER_IMAGE",
"datastrato/gravitino-ci-trino:0.1.5")
environment("GRAVITINO_CI_KAFKA_DOCKER_IMAGE", "apache/kafka:3.7.0")
environment("GRAVITINO_CI_DORIS_DOCKER_IMAGE",
"datastrato/gravitino-ci-doris:0.1.5")
- environment("GRAVITINO_CI_RANGER_DOCKER_IMAGE",
"datastrato/gravitino-ci-ranger:0.1.0")
+ environment("GRAVITINO_CI_RANGER_DOCKER_IMAGE",
"datastrato/gravitino-ci-ranger:0.1.1")
copy {
from("${project.rootDir}/dev/docker/trino/conf")
diff --git
a/integration-test/src/test/java/com/datastrato/gravitino/integration/test/authorization/ranger/RangerIT.java
b/integration-test/src/test/java/com/datastrato/gravitino/integration/test/authorization/ranger/RangerIT.java
index 4f0a312c2..4922c26ae 100644
---
a/integration-test/src/test/java/com/datastrato/gravitino/integration/test/authorization/ranger/RangerIT.java
+++
b/integration-test/src/test/java/com/datastrato/gravitino/integration/test/authorization/ranger/RangerIT.java
@@ -18,9 +18,12 @@
*/
package com.datastrato.gravitino.integration.test.authorization.ranger;
+import static org.apache.ranger.plugin.util.SearchFilter.SERVICE_NAME;
+import static org.apache.ranger.plugin.util.SearchFilter.SERVICE_TYPE;
+
import com.datastrato.gravitino.integration.test.container.ContainerSuite;
import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ranger.RangerClient;
@@ -34,8 +37,10 @@ import org.junit.jupiter.api.Test;
@Tag("gravitino-docker-test")
public class RangerIT {
- private static final String serviceName = "trino-test";
+ private static final String trinoServiceName = "trinodev";
private static final String trinoType = "trino";
+ private static final String hiveServiceName = "hivedev";
+ private static final String hiveType = "hive";
private static RangerClient rangerClient;
private static final ContainerSuite containerSuite =
ContainerSuite.getInstance();
@@ -50,7 +55,8 @@ public class RangerIT {
@AfterAll
public static void cleanup() throws RangerServiceException {
if (rangerClient != null) {
- rangerClient.deleteService(serviceName);
+ rangerClient.deleteService(trinoServiceName);
+ rangerClient.deleteService(hiveServiceName);
}
}
@@ -65,7 +71,7 @@ public class RangerIT {
RangerService service = new RangerService();
service.setType(trinoType);
- service.setName(serviceName);
+ service.setName(trinoServiceName);
service.setConfigs(
ImmutableMap.<String, String>builder()
.put(usernameKey, usernameVal)
@@ -76,12 +82,50 @@ public class RangerIT {
RangerService createdService = rangerClient.createService(service);
Assertions.assertNotNull(createdService);
- Map<String, String> filter = Collections.emptyMap();
+ Map<String, String> filter = new HashMap<>();
+ filter.put(SERVICE_TYPE, trinoType);
+ filter.put(SERVICE_NAME, trinoServiceName);
List<RangerService> services = rangerClient.findServices(filter);
- Assertions.assertEquals(services.get(0).getName(), serviceName);
+ Assertions.assertEquals(services.get(0).getName(), trinoServiceName);
Assertions.assertEquals(services.get(0).getType(), trinoType);
Assertions.assertEquals(services.get(0).getConfigs().get(usernameKey),
usernameVal);
Assertions.assertEquals(services.get(0).getConfigs().get(jdbcKey),
jdbcVal);
Assertions.assertEquals(services.get(0).getConfigs().get(jdbcUrlKey),
jdbcUrlVal);
}
+
+ @Test
+ public void createHiveService() throws RangerServiceException {
+ String usernameKey = "username";
+ String usernameVal = "admin";
+ String passwordKey = "password";
+ String passwordVal = "admin";
+ String jdbcKey = "jdbc.driverClassName";
+ String jdbcVal = "org.apache.hive.jdbc.HiveDriver";
+ String jdbcUrlKey = "jdbc.url";
+ String jdbcUrlVal = "jdbc:hive2://172.17.0.2:10000";
+
+ RangerService service = new RangerService();
+ service.setType(hiveType);
+ service.setName(hiveServiceName);
+ service.setConfigs(
+ ImmutableMap.<String, String>builder()
+ .put(usernameKey, usernameVal)
+ .put(passwordKey, passwordVal)
+ .put(jdbcKey, jdbcVal)
+ .put(jdbcUrlKey, jdbcUrlVal)
+ .build());
+
+ RangerService createdService = rangerClient.createService(service);
+ Assertions.assertNotNull(createdService);
+
+ Map<String, String> filter = new HashMap<>();
+ filter.put(SERVICE_TYPE, hiveType);
+ filter.put(SERVICE_NAME, hiveServiceName);
+ List<RangerService> services = rangerClient.findServices(filter);
+ Assertions.assertEquals(services.get(0).getName(), hiveServiceName);
+ Assertions.assertEquals(services.get(0).getType(), hiveType);
+ Assertions.assertEquals(services.get(0).getConfigs().get(usernameKey),
usernameVal);
+ Assertions.assertEquals(services.get(0).getConfigs().get(jdbcKey),
jdbcVal);
+ Assertions.assertEquals(services.get(0).getConfigs().get(jdbcUrlKey),
jdbcUrlVal);
+ }
}