From 8a6b890a94aa0c6861e274c3cdfb5e09829d81ac Mon Sep 17 00:00:00 2001 From: Xun Date: Mon, 21 Oct 2024 11:05:08 +0800 Subject: [PATCH] [#5116][#5106][#4616][#5135] improve(auth-ranger): The owner of catalog/metalake should have all the privileges of schemas/tables (#5113) ### What changes were proposed in this pull request? The owner of catalog/metalake should have all the privileges of schemas/tables. ### Why are the changes needed? Fix: - #5116 - #5106 - #4616 - #5135 ### Does this PR introduce _any_ user-facing change? N/A ### How was this patch tested? Add ITs. --- .../access-control-integration-test.yml | 6 +- .../org/apache/gravitino/MetadataObjects.java | 8 +- .../ranger/RangerAuthorizationHivePlugin.java | 293 ++++++- .../ranger/RangerAuthorizationPlugin.java | 328 ++++--- .../ranger/RangerClientExtension.java | 15 + .../authorization/ranger/RangerHelper.java | 246 +++--- .../authorization/ranger/RangerPrivilege.java | 59 +- .../ranger/RangerPrivileges.java | 103 ++- .../RangerPrivilegesMappingProvider.java | 43 +- .../ranger/RangerSecurableObject.java | 42 + .../ranger/RangerSecurableObjects.java | 62 ++ .../ranger/TestRangerAuthorizationPlugin.java | 666 +++++++++++++++ .../integration/test/RangerHiveE2EIT.java | 36 +- .../ranger/integration/test/RangerHiveIT.java | 797 ++++++++++++------ .../ranger/integration/test/RangerITEnv.java | 114 ++- build.gradle.kts | 1 + 16 files changed, 2213 insertions(+), 606 deletions(-) create mode 100644 authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObject.java create mode 100644 authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObjects.java create mode 100644 authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/TestRangerAuthorizationPlugin.java diff --git a/.github/workflows/access-control-integration-test.yml b/.github/workflows/access-control-integration-test.yml index e9c55728ab8..ad347aa7e60 100644 --- a/.github/workflows/access-control-integration-test.yml +++ b/.github/workflows/access-control-integration-test.yml @@ -87,9 +87,9 @@ jobs: - name: Authorization Integration Test (JDK${{ matrix.java-version }}) id: integrationTest run: | - ./gradlew -PskipTests -PtestMode=embedded -PjdbcBackend=h2 -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.integration.test.**" - ./gradlew -PskipTests -PtestMode=deploy -PjdbcBackend=mysql -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.integration.test.**" - ./gradlew -PskipTests -PtestMode=deploy -PjdbcBackend=postgresql -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.integration.test.**" + ./gradlew -PtestMode=embedded -PjdbcBackend=h2 -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.**" + ./gradlew -PtestMode=deploy -PjdbcBackend=mysql -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.**" + ./gradlew -PtestMode=deploy -PjdbcBackend=postgresql -PjdkVersion=${{ matrix.java-version }} -PskipDockerTests=false :authorizations:authorization-ranger:test --tests "org.apache.gravitino.authorization.ranger.**" - name: Upload integrate tests reports uses: actions/upload-artifact@v3 diff --git a/api/src/main/java/org/apache/gravitino/MetadataObjects.java b/api/src/main/java/org/apache/gravitino/MetadataObjects.java index 9fb54eada2b..a1303649707 100644 --- a/api/src/main/java/org/apache/gravitino/MetadataObjects.java +++ b/api/src/main/java/org/apache/gravitino/MetadataObjects.java @@ -153,7 +153,13 @@ public static MetadataObject parse(String fullName, MetadataObject.Type type) { return MetadataObjects.of(parts, type); } - private static String getParentFullName(List names) { + /** + * Get the parent full name of the given full name. + * + * @param names The names of the metadata object + * @return The parent full name if it exists, otherwise null + */ + public static String getParentFullName(List names) { if (names.size() <= 1) { return null; } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationHivePlugin.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationHivePlugin.java index a9b08c8669f..838385c8a95 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationHivePlugin.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationHivePlugin.java @@ -18,17 +18,31 @@ */ package org.apache.gravitino.authorization.ranger; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.gravitino.MetadataObject; import org.apache.gravitino.authorization.Privilege; -import org.apache.gravitino.authorization.ranger.RangerPrivilege.RangerHivePrivilege; +import org.apache.gravitino.authorization.SecurableObject; +import org.apache.gravitino.authorization.SecurableObjects; +import org.apache.gravitino.authorization.ranger.RangerPrivileges.RangerHivePrivilege; import org.apache.gravitino.authorization.ranger.reference.RangerDefines.PolicyResource; +import org.apache.gravitino.exceptions.AuthorizationPluginException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAuthorizationHivePlugin extends RangerAuthorizationPlugin { + private static final Logger LOG = LoggerFactory.getLogger(RangerAuthorizationHivePlugin.class); private static volatile RangerAuthorizationHivePlugin instance = null; private RangerAuthorizationHivePlugin(Map config) { @@ -46,11 +60,18 @@ public static synchronized RangerAuthorizationHivePlugin getInstance(Map> privilegesMappingRule() { return ImmutableMap.of( + Privilege.Name.CREATE_CATALOG, + ImmutableSet.of(RangerHivePrivilege.CREATE), + Privilege.Name.USE_CATALOG, + ImmutableSet.of(RangerHivePrivilege.SELECT), Privilege.Name.CREATE_SCHEMA, ImmutableSet.of(RangerHivePrivilege.CREATE), + Privilege.Name.USE_SCHEMA, + ImmutableSet.of(RangerHivePrivilege.SELECT), Privilege.Name.CREATE_TABLE, ImmutableSet.of(RangerHivePrivilege.CREATE), Privilege.Name.MODIFY_TABLE, @@ -60,11 +81,13 @@ public Map> privilegesMappingRule() { ImmutableSet.of(RangerHivePrivilege.READ, RangerHivePrivilege.SELECT)); } + @Override /** Set the default owner rule. */ public Set ownerMappingRule() { return ImmutableSet.of(RangerHivePrivilege.ALL); } + @Override /** Set Ranger policy resource rule. */ public List policyResourceDefinesRule() { return ImmutableList.of( @@ -72,4 +95,272 @@ public List policyResourceDefinesRule() { PolicyResource.TABLE.getName(), PolicyResource.COLUMN.getName()); } + + @Override + /** Allow privilege operation defines rule. */ + public Set allowPrivilegesRule() { + return ImmutableSet.of( + Privilege.Name.CREATE_CATALOG, + Privilege.Name.USE_CATALOG, + Privilege.Name.CREATE_SCHEMA, + Privilege.Name.USE_SCHEMA, + Privilege.Name.CREATE_TABLE, + Privilege.Name.MODIFY_TABLE, + Privilege.Name.SELECT_TABLE); + } + + /** Translate the Gravitino securable object to the Ranger owner securable object. */ + public List translateOwner(MetadataObject metadataObject) { + List rangerSecurableObjects = new ArrayList<>(); + + switch (metadataObject.type()) { + case METALAKE: + case CATALOG: + // Add `*` for the SCHEMA permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(RangerHelper.RESOURCE_ALL), + MetadataObject.Type.SCHEMA, + ownerMappingRule())); + // Add `*.*` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(RangerHelper.RESOURCE_ALL, RangerHelper.RESOURCE_ALL), + MetadataObject.Type.TABLE, + ownerMappingRule())); + // Add `*.*.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL), + MetadataObject.Type.COLUMN, + ownerMappingRule())); + break; + case SCHEMA: + // Add `{schema}` for the SCHEMA permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(metadataObject.name() /*Schema name*/), + MetadataObject.Type.SCHEMA, + ownerMappingRule())); + // Add `{schema}.*` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(metadataObject.name() /*Schema name*/, RangerHelper.RESOURCE_ALL), + MetadataObject.Type.TABLE, + ownerMappingRule())); + // Add `{schema}.*.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + metadataObject.name() /*Schema name*/, + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL), + MetadataObject.Type.COLUMN, + ownerMappingRule())); + break; + case TABLE: + // Add `{schema}.{table}` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + convertToRangerMetadataObject(metadataObject), + MetadataObject.Type.TABLE, + ownerMappingRule())); + // Add `{schema}.{table}.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + Stream.concat( + convertToRangerMetadataObject(metadataObject).stream(), + Stream.of(RangerHelper.RESOURCE_ALL)) + .collect(Collectors.toList()), + MetadataObject.Type.COLUMN, + ownerMappingRule())); + break; + default: + throw new AuthorizationPluginException( + "The owner privilege is not supported for the securable object: %s", + metadataObject.type()); + } + + return rangerSecurableObjects; + } + + /** Translate the Gravitino securable object to the Ranger securable object. */ + public List translatePrivilege(SecurableObject securableObject) { + List rangerSecurableObjects = new ArrayList<>(); + + securableObject.privileges().stream() + .filter(Objects::nonNull) + .forEach( + gravitinoPrivilege -> { + Set rangerPrivileges = new HashSet<>(); + privilegesMappingRule().get(gravitinoPrivilege.name()).stream() + .forEach( + rangerPrivilege -> + rangerPrivileges.add( + new RangerPrivileges.RangerHivePrivilegeImpl( + rangerPrivilege, gravitinoPrivilege.condition()))); + + switch (gravitinoPrivilege.name()) { + case CREATE_CATALOG: + // Ignore the Gravitino privilege `CREATE_CATALOG` in the + // RangerAuthorizationHivePlugin + break; + case USE_CATALOG: + switch (securableObject.type()) { + case METALAKE: + case CATALOG: + // Add Ranger privilege(`SELECT`) to SCHEMA(`*`) + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(RangerHelper.RESOURCE_ALL), + MetadataObject.Type.SCHEMA, + rangerPrivileges)); + break; + default: + throw new AuthorizationPluginException( + "The privilege %s is not supported for the securable object: %s", + gravitinoPrivilege.name(), securableObject.type()); + } + break; + case CREATE_SCHEMA: + switch (securableObject.type()) { + case METALAKE: + case CATALOG: + // Add Ranger privilege(`CREATE`) to SCHEMA(`*`) + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(RangerHelper.RESOURCE_ALL), + MetadataObject.Type.SCHEMA, + rangerPrivileges)); + break; + default: + throw new AuthorizationPluginException( + "The privilege %s is not supported for the securable object: %s", + gravitinoPrivilege.name(), securableObject.type()); + } + break; + case USE_SCHEMA: + switch (securableObject.type()) { + case METALAKE: + case CATALOG: + // Add Ranger privilege(`SELECT`) to SCHEMA(`*`) + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(RangerHelper.RESOURCE_ALL), + MetadataObject.Type.SCHEMA, + rangerPrivileges)); + break; + case SCHEMA: + // Add Ranger privilege(`SELECT`) to SCHEMA(`{schema}`) + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of(securableObject.name() /*Schema name*/), + MetadataObject.Type.SCHEMA, + rangerPrivileges)); + break; + default: + throw new AuthorizationPluginException( + "The privilege %s is not supported for the securable object: %s", + gravitinoPrivilege.name(), securableObject.type()); + } + break; + case CREATE_TABLE: + case MODIFY_TABLE: + case SELECT_TABLE: + switch (securableObject.type()) { + case METALAKE: + case CATALOG: + // Add `*.*` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + RangerHelper.RESOURCE_ALL, RangerHelper.RESOURCE_ALL), + MetadataObject.Type.TABLE, + rangerPrivileges)); + // Add `*.*.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL), + MetadataObject.Type.COLUMN, + rangerPrivileges)); + break; + case SCHEMA: + // Add `{schema}.*` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + securableObject.name() /*Schema name*/, + RangerHelper.RESOURCE_ALL), + MetadataObject.Type.TABLE, + rangerPrivileges)); + // Add `{schema}.*.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + ImmutableList.of( + securableObject.name() /*Schema name*/, + RangerHelper.RESOURCE_ALL, + RangerHelper.RESOURCE_ALL), + MetadataObject.Type.COLUMN, + rangerPrivileges)); + break; + case TABLE: + if (gravitinoPrivilege.name() == Privilege.Name.CREATE_TABLE) { + throw new AuthorizationPluginException( + "The privilege %s is not supported for the securable object: %s", + gravitinoPrivilege.name(), securableObject.type()); + } else { + // Add `{schema}.{table}` for the TABLE permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + convertToRangerMetadataObject(securableObject), + MetadataObject.Type.TABLE, + rangerPrivileges)); + // Add `{schema}.{table}.*` for the COLUMN permission + rangerSecurableObjects.add( + RangerSecurableObjects.of( + Stream.concat( + convertToRangerMetadataObject(securableObject).stream(), + Stream.of(RangerHelper.RESOURCE_ALL)) + .collect(Collectors.toList()), + MetadataObject.Type.COLUMN, + rangerPrivileges)); + } + break; + default: + LOG.warn( + "RangerAuthorizationHivePlugin -> privilege {} is not supported for the securable object: {}", + gravitinoPrivilege.name(), + securableObject.type()); + } + break; + default: + LOG.warn( + "RangerAuthorizationHivePlugin -> privilege {} is not supported for the securable object: {}", + gravitinoPrivilege.name(), + securableObject.type()); + } + }); + + return rangerSecurableObjects; + } + + /** + * Because the Ranger securable object is different from the Gravitino securable object, we need + * to convert the Gravitino securable object to the Ranger securable object. + */ + List convertToRangerMetadataObject(MetadataObject metadataObject) { + Preconditions.checkArgument( + !(metadataObject instanceof RangerPrivileges), + "The metadata object must be not a RangerPrivileges object."); + List nsMetadataObject = + Lists.newArrayList(SecurableObjects.DOT_SPLITTER.splitToList(metadataObject.fullName())); + nsMetadataObject.remove(0); // remove the catalog name + return nsMetadataObject; + } } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java index 021fca2a996..b188b55dd57 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerAuthorizationPlugin.java @@ -19,21 +19,24 @@ package org.apache.gravitino.authorization.ranger; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.time.Instant; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import org.apache.gravitino.MetadataObject; import org.apache.gravitino.authorization.Group; import org.apache.gravitino.authorization.Owner; -import org.apache.gravitino.authorization.Privilege; import org.apache.gravitino.authorization.Role; import org.apache.gravitino.authorization.RoleChange; +import org.apache.gravitino.authorization.SecurableObject; import org.apache.gravitino.authorization.User; import org.apache.gravitino.authorization.ranger.reference.VXGroup; import org.apache.gravitino.authorization.ranger.reference.VXGroupList; @@ -41,6 +44,7 @@ import org.apache.gravitino.authorization.ranger.reference.VXUserList; import org.apache.gravitino.connector.AuthorizationPropertiesMeta; import org.apache.gravitino.connector.authorization.AuthorizationPlugin; +import org.apache.gravitino.exceptions.AuthorizationPluginException; import org.apache.gravitino.meta.AuditInfo; import org.apache.gravitino.meta.GroupEntity; import org.apache.gravitino.meta.UserEntity; @@ -78,11 +82,11 @@ protected RangerAuthorizationPlugin(Map config) { // Apache Ranger Password should be minimum 8 characters with min one alphabet and one numeric. String password = config.get(AuthorizationPropertiesMeta.RANGER_PASSWORD); rangerServiceName = config.get(AuthorizationPropertiesMeta.RANGER_SERVICE_NAME); - RangerHelper.check(rangerUrl != null, "Ranger admin URL is required"); - RangerHelper.check(authType != null, "Ranger auth type is required"); - RangerHelper.check(rangerAdminName != null, "Ranger username is required"); - RangerHelper.check(password != null, "Ranger password is required"); - RangerHelper.check(rangerServiceName != null, "Ranger service name is required"); + Preconditions.checkArgument(rangerUrl != null, "Ranger admin URL is required"); + Preconditions.checkArgument(authType != null, "Ranger auth type is required"); + Preconditions.checkArgument(rangerAdminName != null, "Ranger username is required"); + Preconditions.checkArgument(password != null, "Ranger password is required"); + Preconditions.checkArgument(rangerServiceName != null, "Ranger service name is required"); rangerClient = new RangerClientExtension(rangerUrl, authType, rangerAdminName, password); rangerHelper = @@ -90,21 +94,10 @@ protected RangerAuthorizationPlugin(Map config) { rangerClient, rangerAdminName, rangerServiceName, - privilegesMappingRule(), ownerMappingRule(), policyResourceDefinesRule()); } - /** - * Translate the privilege name to the corresponding privilege name in the Ranger - * - * @param name The privilege name to translate - * @return The corresponding Ranger privilege name in the underlying permission system - */ - public Set translatePrivilege(Privilege.Name name) { - return rangerHelper.translatePrivilege(name); - } - /** * Create a new role in the Ranger.
* 1. Create a policy for metadata object.
@@ -112,7 +105,11 @@ public Set translatePrivilege(Privilege.Name name) { */ @Override public Boolean onRoleCreated(Role role) throws RuntimeException { - rangerHelper.createRangerRoleIfNotExists(role.name()); + if (!validAuthorizationOperation(role.securableObjects())) { + return false; + } + + rangerHelper.createRangerRoleIfNotExists(role.name(), false); return onRoleUpdated( role, role.securableObjects().stream() @@ -122,12 +119,18 @@ public Boolean onRoleCreated(Role role) throws RuntimeException { @Override public Boolean onRoleAcquired(Role role) throws RuntimeException { + if (!validAuthorizationOperation(role.securableObjects())) { + return false; + } return rangerHelper.checkRangerRole(role.name()); } /** Remove the role name from the Ranger policy item, and delete this Role in the Ranger.
*/ @Override public Boolean onRoleDeleted(Role role) throws RuntimeException { + if (!validAuthorizationOperation(role.securableObjects())) { + return false; + } // First, remove the role in the Ranger policy onRoleUpdated( role, @@ -147,21 +150,73 @@ public Boolean onRoleDeleted(Role role) throws RuntimeException { @Override public Boolean onRoleUpdated(Role role, RoleChange... changes) throws RuntimeException { for (RoleChange change : changes) { - boolean execResult; if (change instanceof RoleChange.AddSecurableObject) { - execResult = doAddSecurableObject((RoleChange.AddSecurableObject) change); + SecurableObject securableObject = + ((RoleChange.AddSecurableObject) change).getSecurableObject(); + if (!validAuthorizationOperation(Arrays.asList(securableObject))) { + return false; + } + + List rangerSecurableObjects = translatePrivilege(securableObject); + rangerSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + if (!doAddSecurableObject(role.name(), rangerSecurableObject)) { + throw new RuntimeException( + "Failed to add the securable object to the Ranger policy!"); + } + }); } else if (change instanceof RoleChange.RemoveSecurableObject) { - execResult = doRemoveSecurableObject((RoleChange.RemoveSecurableObject) change); + SecurableObject securableObject = + ((RoleChange.RemoveSecurableObject) change).getSecurableObject(); + if (!validAuthorizationOperation(Arrays.asList(securableObject))) { + return false; + } + + List rangerSecurableObjects = translatePrivilege(securableObject); + rangerSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + if (!doRemoveSecurableObject(role.name(), rangerSecurableObject)) { + throw new RuntimeException( + "Failed to add the securable object to the Ranger policy!"); + } + }); } else if (change instanceof RoleChange.UpdateSecurableObject) { - execResult = doUpdateSecurableObject((RoleChange.UpdateSecurableObject) change); + SecurableObject oldSecurableObject = + ((RoleChange.UpdateSecurableObject) change).getSecurableObject(); + if (!validAuthorizationOperation(Arrays.asList(oldSecurableObject))) { + return false; + } + SecurableObject newSecurableObject = + ((RoleChange.UpdateSecurableObject) change).getNewSecurableObject(); + if (!validAuthorizationOperation(Arrays.asList(newSecurableObject))) { + return false; + } + + Preconditions.checkArgument( + (oldSecurableObject.fullName().equals(newSecurableObject.fullName()) + && oldSecurableObject.type().equals(newSecurableObject.type())), + "The old and new securable objects metadata must be equal!"); + List rangerOldSecurableObjects = + translatePrivilege(oldSecurableObject); + List rangerNewSecurableObjects = + translatePrivilege(newSecurableObject); + rangerOldSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + doRemoveSecurableObject(role.name(), rangerSecurableObject); + }); + rangerNewSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + doAddSecurableObject(role.name(), rangerSecurableObject); + }); } else { throw new IllegalArgumentException( "Unsupported role change type: " + (change == null ? "null" : change.getClass().getSimpleName())); } - if (!execResult) { - return Boolean.FALSE; - } } return Boolean.TRUE; @@ -181,29 +236,42 @@ public Boolean onRoleUpdated(Role role, RoleChange... changes) throws RuntimeExc @Override public Boolean onOwnerSet(MetadataObject metadataObject, Owner preOwner, Owner newOwner) throws RuntimeException { - RangerHelper.check(newOwner != null, "The newOwner must be not null"); + Preconditions.checkArgument(newOwner != null, "The newOwner must be not null"); // Add the user or group to the Ranger + String preOwnerUserName = null, + preOwnerGroupName = null, + newOwnerUserName = null, + newOwnerGroupName = null; AuditInfo auditInfo = AuditInfo.builder() .withCreator(PrincipalUtils.getCurrentPrincipal().getName()) .withCreateTime(Instant.now()) .build(); + if (preOwner != null) { + if (preOwner.type() == Owner.Type.USER) { + preOwnerUserName = newOwner.name(); + } else { + preOwnerGroupName = newOwner.name(); + } + } if (newOwner.type() == Owner.Type.USER) { + newOwnerUserName = newOwner.name(); UserEntity userEntity = UserEntity.builder() .withId(1L) - .withName(newOwner.name()) + .withName(newOwnerUserName) .withRoleNames(Collections.emptyList()) .withRoleIds(Collections.emptyList()) .withAuditInfo(auditInfo) .build(); onUserAdded(userEntity); } else { + newOwnerGroupName = newOwner.name(); GroupEntity groupEntity = GroupEntity.builder() .withId(1L) - .withName(newOwner.name()) + .withName(newOwnerGroupName) .withRoleNames(Collections.emptyList()) .withRoleIds(Collections.emptyList()) .withAuditInfo(auditInfo) @@ -211,17 +279,79 @@ public Boolean onOwnerSet(MetadataObject metadataObject, Owner preOwner, Owner n onGroupAdded(groupEntity); } - RangerPolicy policy = rangerHelper.findManagedPolicy(metadataObject); - try { - if (policy == null) { - policy = rangerHelper.addOwnerToNewPolicy(metadataObject, newOwner); - rangerClient.createPolicy(policy); - } else { - rangerHelper.updatePolicyOwner(policy, preOwner, newOwner); - rangerClient.updatePolicy(policy.getId(), policy); - } - } catch (RangerServiceException e) { - throw new RuntimeException(e); + List rangerSecurableObjects = translateOwner(metadataObject); + String ownerRoleName; + switch (metadataObject.type()) { + case METALAKE: + case CATALOG: + // The metalake and catalog use role to manage the owner + if (metadataObject.type() == MetadataObject.Type.METALAKE) { + ownerRoleName = RangerHelper.GRAVITINO_METALAKE_OWNER_ROLE; + } else { + ownerRoleName = RangerHelper.GRAVITINO_CATALOG_OWNER_ROLE; + } + rangerHelper.createRangerRoleIfNotExists(ownerRoleName, true); + try { + if (preOwnerUserName != null || preOwnerGroupName != null) { + GrantRevokeRoleRequest revokeRoleRequest = + rangerHelper.createGrantRevokeRoleRequest( + ownerRoleName, preOwnerUserName, preOwnerGroupName); + rangerClient.revokeRole(rangerServiceName, revokeRoleRequest); + } + if (newOwnerUserName != null || newOwnerGroupName != null) { + GrantRevokeRoleRequest grantRoleRequest = + rangerHelper.createGrantRevokeRoleRequest( + ownerRoleName, newOwnerUserName, newOwnerGroupName); + rangerClient.grantRole(rangerServiceName, grantRoleRequest); + } + } catch (RangerServiceException e) { + // Ignore exception, support idempotent operation + LOG.warn("Grant owner role: {} failed!", ownerRoleName, e); + } + + rangerSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + RangerPolicy policy = rangerHelper.findManagedPolicy(rangerSecurableObject); + try { + if (policy == null) { + policy = + rangerHelper.addOwnerRoleToNewPolicy( + rangerSecurableObject, ownerRoleName); + rangerClient.createPolicy(policy); + } else { + rangerHelper.updatePolicyOwnerRole(policy, ownerRoleName); + rangerClient.updatePolicy(policy.getId(), policy); + } + } catch (RangerServiceException e) { + throw new RuntimeException(e); + } + }); + break; + case SCHEMA: + case TABLE: + // The schema and table use user/group to manage the owner + rangerSecurableObjects.stream() + .forEach( + rangerSecurableObject -> { + RangerPolicy policy = rangerHelper.findManagedPolicy(rangerSecurableObject); + try { + if (policy == null) { + policy = rangerHelper.addOwnerToNewPolicy(rangerSecurableObject, newOwner); + rangerClient.createPolicy(policy); + } else { + rangerHelper.updatePolicyOwner(policy, preOwner, newOwner); + rangerClient.updatePolicy(policy.getId(), policy); + } + } catch (RangerServiceException e) { + throw new RuntimeException(e); + } + }); + break; + default: + throw new AuthorizationPluginException( + "The owner privilege is not supported for the securable object: %s", + metadataObject.type()); } return Boolean.TRUE; @@ -238,13 +368,17 @@ public Boolean onOwnerSet(MetadataObject metadataObject, Owner preOwner, Owner n */ @Override public Boolean onGrantedRolesToUser(List roles, User user) throws RuntimeException { + if (roles.stream().anyMatch(role -> !validAuthorizationOperation(role.securableObjects()))) { + return false; + } + // If the user does not exist, then create it. onUserAdded(user); roles.stream() .forEach( role -> { - rangerHelper.createRangerRoleIfNotExists(role.name()); + rangerHelper.createRangerRoleIfNotExists(role.name(), false); GrantRevokeRoleRequest grantRevokeRoleRequest = rangerHelper.createGrantRevokeRoleRequest(role.name(), user.name(), null); try { @@ -269,6 +403,9 @@ public Boolean onGrantedRolesToUser(List roles, User user) throws RuntimeE */ @Override public Boolean onRevokedRolesFromUser(List roles, User user) throws RuntimeException { + if (roles.stream().anyMatch(role -> !validAuthorizationOperation(role.securableObjects()))) { + return false; + } // If the user does not exist, then create it. onUserAdded(user); @@ -300,13 +437,16 @@ public Boolean onRevokedRolesFromUser(List roles, User user) throws Runtim */ @Override public Boolean onGrantedRolesToGroup(List roles, Group group) throws RuntimeException { + if (roles.stream().anyMatch(role -> !validAuthorizationOperation(role.securableObjects()))) { + return false; + } // If the group does not exist, then create it. onGroupAdded(group); roles.stream() .forEach( role -> { - rangerHelper.createRangerRoleIfNotExists(role.name()); + rangerHelper.createRangerRoleIfNotExists(role.name(), false); GrantRevokeRoleRequest grantRevokeRoleRequest = rangerHelper.createGrantRevokeRoleRequest(role.name(), null, group.name()); try { @@ -330,6 +470,9 @@ public Boolean onGrantedRolesToGroup(List roles, Group group) throws Runti */ @Override public Boolean onRevokedRolesFromGroup(List roles, Group group) throws RuntimeException { + if (roles.stream().anyMatch(role -> !validAuthorizationOperation(role.securableObjects()))) { + return false; + } onGroupAdded(group); roles.stream() .forEach( @@ -414,37 +557,32 @@ public Boolean onGroupAcquired(Group group) { * return true.
* 3. If the policy does not exist, then create a new policy.
*/ - private boolean doAddSecurableObject(RoleChange.AddSecurableObject change) { - RangerPolicy policy = rangerHelper.findManagedPolicy(change.getSecurableObject()); + private boolean doAddSecurableObject(String roleName, RangerSecurableObject securableObject) { + RangerPolicy policy = rangerHelper.findManagedPolicy(securableObject); if (policy != null) { - // Check the policy item's accesses and roles equal the Gravitino securable object's privilege - Set policyPrivileges = + // Check the policy item's accesses and roles equal the Ranger securable object's privilege + Set policyPrivileges = policy.getPolicyItems().stream() - .filter(policyItem -> policyItem.getRoles().contains(change.getRoleName())) + .filter(policyItem -> policyItem.getRoles().contains(roleName)) .flatMap(policyItem -> policyItem.getAccesses().stream()) .map(RangerPolicy.RangerPolicyItemAccess::getType) + .map(RangerPrivileges::valueOf) .collect(Collectors.toSet()); - Set newPrivileges = - change.getSecurableObject().privileges().stream() - .filter(Objects::nonNull) - .flatMap(privilege -> translatePrivilege(privilege.name()).stream()) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - if (policyPrivileges.containsAll(newPrivileges)) { + if (policyPrivileges.containsAll(securableObject.privileges())) { LOG.info( "The privilege({}) already added to Ranger policy({})!", policy.getName(), - change.getSecurableObject().fullName()); + securableObject.fullName()); // If it exists policy with the same privilege, then directly return true, because support // idempotent operation. return true; } } else { - policy = rangerHelper.createPolicyAddResources(change.getSecurableObject()); + policy = rangerHelper.createPolicyAddResources(securableObject); } - rangerHelper.addPolicyItem(policy, change.getRoleName(), change.getSecurableObject()); + rangerHelper.addPolicyItem(policy, roleName, securableObject); try { if (policy.getId() == null) { rangerClient.createPolicy(policy); @@ -465,13 +603,13 @@ private boolean doAddSecurableObject(RoleChange.AddSecurableObject change) { *
* 3. If policy does not contain any policy item, then delete this policy.
*/ - private boolean doRemoveSecurableObject(RoleChange.RemoveSecurableObject change) { - RangerPolicy policy = rangerHelper.findManagedPolicy(change.getSecurableObject()); + private boolean doRemoveSecurableObject( + String roleName, RangerSecurableObject rangerSecurableObject) { + RangerPolicy policy = rangerHelper.findManagedPolicy(rangerSecurableObject); if (policy == null) { LOG.warn( - "Cannot find the Ranger policy({}) for the Gravitino securable object({})!", - change.getRoleName(), - change.getSecurableObject().fullName()); + "Cannot find the Ranger policy for the Ranger securable object({})!", + rangerSecurableObject.fullName()); // Don't throw exception or return false, because need support immutable operation. return true; } @@ -487,16 +625,13 @@ private boolean doRemoveSecurableObject(RoleChange.RemoveSecurableObject change) access -> { // Use Gravitino privilege to search the Ranger policy item's access boolean matchPrivilege = - change.getSecurableObject().privileges().stream() + rangerSecurableObject.privileges().stream() .filter(Objects::nonNull) - .flatMap( - privilege -> translatePrivilege(privilege.name()).stream()) - .filter(Objects::nonNull) - .anyMatch(privilege -> privilege.equals(access.getType())); + .anyMatch(privilege -> privilege.equalsTo(access.getType())); return matchPrivilege; }); if (match) { - policyItem.getRoles().removeIf(change.getRoleName()::equals); + policyItem.getRoles().removeIf(roleName::equals); } }); @@ -522,36 +657,35 @@ private boolean doRemoveSecurableObject(RoleChange.RemoveSecurableObject change) return true; } - /** - * 1. Find the policy base the metadata object.
- * 2. If the policy exists, then user new securable object's privilege to update.
- * 3. If the policy does not exist, return false.
- */ - private boolean doUpdateSecurableObject(RoleChange.UpdateSecurableObject change) { - RangerPolicy policy = rangerHelper.findManagedPolicy(change.getSecurableObject()); - if (policy == null) { - LOG.warn( - "Cannot find the Ranger policy({}) for the Gravitino securable object({})!", - change.getRoleName(), - change.getSecurableObject().fullName()); - // Don't throw exception or return false, because need support immutable operation. - return false; - } - - rangerHelper.removePolicyItem(policy, change.getRoleName(), change.getSecurableObject()); - rangerHelper.addPolicyItem(policy, change.getRoleName(), change.getNewSecurableObject()); - try { - if (policy.getId() == null) { - rangerClient.createPolicy(policy); - } else { - rangerClient.updatePolicy(policy.getId(), policy); - } - } catch (RangerServiceException e) { - throw new RuntimeException(e); - } - return true; - } - @Override public void close() throws IOException {} + + boolean validAuthorizationOperation(List securableObjects) { + return securableObjects.stream() + .allMatch( + securableObject -> { + AtomicBoolean match = new AtomicBoolean(true); + securableObject.privileges().stream() + .forEach( + privilege -> { + if (!allowPrivilegesRule().contains(privilege.name())) { + LOG.error( + "Authorization to ignore privilege({}) on metadata object({})!", + privilege.name(), + securableObject.fullName()); + match.set(false); + return; + } + + if (!privilege.canBindTo(securableObject.type())) { + LOG.error( + "The privilege({}) is not supported for the metadata object({})!", + privilege.name(), + securableObject.fullName()); + match.set(false); + } + }); + return match.get(); + }); + } } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java index fd822559da6..a554559ea5c 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerClientExtension.java @@ -18,6 +18,7 @@ */ package org.apache.gravitino.authorization.ranger; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.sun.jersey.api.client.GenericType; import com.sun.jersey.api.client.UniformInterfaceException; @@ -32,6 +33,7 @@ import org.apache.gravitino.authorization.ranger.reference.VXUserList; import org.apache.ranger.RangerClient; import org.apache.ranger.RangerServiceException; +import org.apache.ranger.plugin.model.RangerPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,6 +100,19 @@ public RangerClientExtension(String hostName, String authType, String username, } } + public RangerPolicy createPolicy(RangerPolicy policy) throws RangerServiceException { + Preconditions.checkArgument( + policy.getResources().size() > 0, "Ranger policy resources can not be empty!"); + return super.createPolicy(policy); + } + + public RangerPolicy updatePolicy(long policyId, RangerPolicy policy) + throws RangerServiceException { + Preconditions.checkArgument( + policy.getResources().size() > 0, "Ranger policy resources can not be empty!"); + return super.updatePolicy(policyId, policy); + } + public Boolean createUser(VXUser user) throws RuntimeException { try { callAPIMethodClassResponseType.invoke(this, CREATE_EXTERNAL_USER, null, user, VXUser.class); diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerHelper.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerHelper.java index 13f5a5cba30..12a99fd6161 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerHelper.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerHelper.java @@ -18,22 +18,19 @@ */ package org.apache.gravitino.authorization.ranger; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.google.errorprone.annotations.FormatMethod; -import com.google.errorprone.annotations.FormatString; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.apache.gravitino.MetadataObject; import org.apache.gravitino.authorization.Owner; import org.apache.gravitino.authorization.Privilege; -import org.apache.gravitino.authorization.SecurableObject; import org.apache.gravitino.authorization.SecurableObjects; import org.apache.gravitino.exceptions.AuthorizationPluginException; import org.apache.ranger.RangerClient; @@ -53,9 +50,8 @@ public class RangerHelper { private static final Logger LOG = LoggerFactory.getLogger(RangerHelper.class); public static final String MANAGED_BY_GRAVITINO = "MANAGED_BY_GRAVITINO"; - - /** Mapping Gravitino privilege name to the underlying authorization system privileges. */ - private final Map> privilegesMapping; + /** The `*` gives access to all resources */ + public static final String RESOURCE_ALL = "*"; /** The owner privileges, the owner can do anything on the metadata object */ private final Set ownerPrivileges; /** The policy search keys */ @@ -64,34 +60,22 @@ public class RangerHelper { private final RangerClient rangerClient; private final String rangerAdminName; private final String rangerServiceName; + public static final String GRAVITINO_METALAKE_OWNER_ROLE = "GRAVITINO_METALAKE_OWNER_ROLE"; + public static final String GRAVITINO_CATALOG_OWNER_ROLE = "GRAVITINO_CATALOG_OWNER_ROLE"; public RangerHelper( RangerClient rangerClient, String rangerAdminName, String rangerServiceName, - Map> privilegesMapping, Set ownerPrivileges, List resourceDefines) { this.rangerClient = rangerClient; this.rangerAdminName = rangerAdminName; this.rangerServiceName = rangerServiceName; - this.privilegesMapping = privilegesMapping; this.ownerPrivileges = ownerPrivileges; this.policyResourceDefines = resourceDefines; } - /** - * Translate the privilege name to the corresponding privilege name in the Ranger - * - * @param name The privilege name to translate - * @return The corresponding Ranger privilege name in the underlying permission system - */ - public Set translatePrivilege(Privilege.Name name) { - return privilegesMapping.get(name).stream() - .map(RangerPrivilege::getName) - .collect(Collectors.toSet()); - } - /** * For easy management, each privilege will create one RangerPolicyItemAccess in the policy. * @@ -123,56 +107,47 @@ void checkPolicyItemAccess(RangerPolicy.RangerPolicyItem policyItem) * We cannot clean the policy items because one Ranger policy maybe contains multiple Gravitino * securable objects.
*/ - void addPolicyItem(RangerPolicy policy, String roleName, SecurableObject securableObject) { - // First check the privilege if support in the Ranger Hive - checkPrivileges(securableObject); - + void addPolicyItem(RangerPolicy policy, String roleName, RangerSecurableObject securableObject) { // Add the policy items by the securable object's privileges securableObject .privileges() .forEach( - gravitinoPrivilege -> { - // Translate the Gravitino privilege to map Ranger privilege - translatePrivilege(gravitinoPrivilege.name()) - .forEach( - rangerPrivilege -> { - // Find the policy item that matches Gravitino privilege - List matchPolicyItems = - policy.getPolicyItems().stream() - .filter( - policyItem -> { - return policyItem.getAccesses().stream() - .anyMatch( - access -> access.getType().equals(rangerPrivilege)); - }) - .collect(Collectors.toList()); - - if (matchPolicyItems.size() == 0) { - // If the policy item does not exist, then create a new policy item - RangerPolicy.RangerPolicyItem policyItem = - new RangerPolicy.RangerPolicyItem(); - RangerPolicy.RangerPolicyItemAccess access = - new RangerPolicy.RangerPolicyItemAccess(); - access.setType(rangerPrivilege); - policyItem.getAccesses().add(access); - policyItem.getRoles().add(roleName); - if (Privilege.Condition.ALLOW == gravitinoPrivilege.condition()) { - policy.getPolicyItems().add(policyItem); - } else { - policy.getDenyPolicyItems().add(policyItem); + rangerPrivilege -> { + // Find the policy item that matches Gravitino privilege + List matchPolicyItems = + policy.getPolicyItems().stream() + .filter( + policyItem -> { + return policyItem.getAccesses().stream() + .anyMatch( + access -> access.getType().equals(rangerPrivilege.getName())); + }) + .collect(Collectors.toList()); + + if (matchPolicyItems.isEmpty()) { + // If the policy item does not exist, then create a new policy item + RangerPolicy.RangerPolicyItem policyItem = new RangerPolicy.RangerPolicyItem(); + RangerPolicy.RangerPolicyItemAccess access = + new RangerPolicy.RangerPolicyItemAccess(); + access.setType(rangerPrivilege.getName()); + policyItem.getAccesses().add(access); + policyItem.getRoles().add(roleName); + if (Privilege.Condition.ALLOW == rangerPrivilege.condition()) { + policy.getPolicyItems().add(policyItem); + } else { + policy.getDenyPolicyItems().add(policyItem); + } + } else { + // If the policy item exists, then add the role to the policy item + matchPolicyItems.stream() + .forEach( + policyItem -> { + // If the role is not in the policy item, then add it + if (!policyItem.getRoles().contains(roleName)) { + policyItem.getRoles().add(roleName); } - } else { - // If the policy item exists, then add the role to the policy item - matchPolicyItems.stream() - .forEach( - policyItem -> { - // If the role is not in the policy item, then add it - if (!policyItem.getRoles().contains(roleName)) { - policyItem.getRoles().add(roleName); - } - }); - } - }); + }); + } }); } @@ -181,10 +156,8 @@ void addPolicyItem(RangerPolicy policy, String roleName, SecurableObject securab * We cannot directly clean the policy items because one Ranger policy maybe contains multiple * Gravitino privilege objects.
*/ - void removePolicyItem(RangerPolicy policy, String roleName, SecurableObject securableObject) { - // First check the privilege if support in the Ranger Hive - checkPrivileges(securableObject); - + void removePolicyItem( + RangerPolicy policy, String roleName, RangerSecurableObject securableObject) { // Delete the policy role base the securable object's privileges policy.getPolicyItems().stream() .forEach( @@ -195,12 +168,9 @@ void removePolicyItem(RangerPolicy policy, String roleName, SecurableObject secu access -> { boolean matchPrivilege = securableObject.privileges().stream() - .filter(Objects::nonNull) - .flatMap(privilege -> translatePrivilege(privilege.name()).stream()) - .filter(Objects::nonNull) .anyMatch( privilege -> { - return access.getType().equals(privilege); + return access.getType().equals(privilege.getName()); }); if (matchPrivilege) { policyItem.getRoles().removeIf(roleName::equals); @@ -220,33 +190,14 @@ void removePolicyItem(RangerPolicy policy, String roleName, SecurableObject secu } /** - * Whether this privilege is underlying permission system supported - * - * @param securableObject The securable object to check - * @return true if the privilege is supported, otherwise false - */ - private boolean checkPrivileges(SecurableObject securableObject) { - securableObject - .privileges() - .forEach( - privilege -> { - check( - privilegesMapping.containsKey(privilege.name()), - "This privilege %s is not supported in the Ranger hive authorization", - privilege.name()); - }); - return true; - } - - /** - * Find the managed policy for the metadata object. + * Find the managed policy for the ranger securable object. * - * @param metadataObject The metadata object to find the managed policy. + * @param rangerSecurableObject The ranger securable object to find the managed policy. * @return The managed policy for the metadata object. */ - public RangerPolicy findManagedPolicy(MetadataObject metadataObject) + public RangerPolicy findManagedPolicy(RangerSecurableObject rangerSecurableObject) throws AuthorizationPluginException { - List nsMetadataObj = getMetadataObjectNames(metadataObject); + List nsMetadataObj = getMetadataObjectNames(rangerSecurableObject); Map searchFilters = new HashMap<>(); Map preciseFilters = new HashMap<>(); @@ -323,6 +274,10 @@ protected GrantRevokeRoleRequest createGrantRevokeRoleRequest( Set groups = StringUtils.isEmpty(groupName) ? Sets.newHashSet() : Sets.newHashSet(groupName); + if (users.size() == 0 && groups.size() == 0) { + throw new AuthorizationPluginException("The user and group cannot be empty!"); + } + GrantRevokeRoleRequest roleRequest = new GrantRevokeRoleRequest(); roleRequest.setUsers(users); roleRequest.setGroups(groups); @@ -331,8 +286,25 @@ protected GrantRevokeRoleRequest createGrantRevokeRoleRequest( return roleRequest; } - /** Create a Ranger role if the role does not exist. */ - protected RangerRole createRangerRoleIfNotExists(String roleName) { + /** + * Create a Ranger role if the role does not exist. + * + * @param roleName The role name to create + * @param isOwnerRole The role is owner role or not + */ + protected RangerRole createRangerRoleIfNotExists(String roleName, boolean isOwnerRole) { + if (isOwnerRole) { + Preconditions.checkArgument( + roleName.equalsIgnoreCase(GRAVITINO_METALAKE_OWNER_ROLE) + || roleName.equalsIgnoreCase(GRAVITINO_CATALOG_OWNER_ROLE), + "The role name should be GRAVITINO_METALAKE_OWNER_ROLE or GRAVITINO_CATALOG_OWNER_ROLE"); + } else { + Preconditions.checkArgument( + !roleName.equalsIgnoreCase(GRAVITINO_METALAKE_OWNER_ROLE) + && !roleName.equalsIgnoreCase(GRAVITINO_CATALOG_OWNER_ROLE), + "The role name should not be GRAVITINO_METALAKE_OWNER_ROLE or GRAVITINO_CATALOG_OWNER_ROLE"); + } + RangerRole rangerRole = null; try { rangerRole = rangerClient.getRole(roleName, rangerAdminName, rangerServiceName); @@ -423,14 +395,13 @@ protected void updatePolicyOwner(RangerPolicy policy, Owner preOwner, Owner newO }); } - private List getMetadataObjectNames(MetadataObject metadataObject) { + private static List getMetadataObjectNames(MetadataObject metadataObject) { List nsMetadataObject = Lists.newArrayList(SecurableObjects.DOT_SPLITTER.splitToList(metadataObject.fullName())); if (nsMetadataObject.size() > 4) { // The max level of the securable object is `catalog.db.table.column` throw new RuntimeException("The length of the securable object should not be greater than 4"); } - nsMetadataObject.remove(0); // remove `catalog` return nsMetadataObject; } @@ -472,10 +443,73 @@ protected RangerPolicy addOwnerToNewPolicy(MetadataObject metadataObject, Owner return policy; } - @FormatMethod - protected static void check(boolean condition, @FormatString String message, Object... args) { - if (!condition) { - throw new AuthorizationPluginException(message, args); - } + protected RangerPolicy addOwnerRoleToNewPolicy( + MetadataObject metadataObject, String ownerRoleName) { + RangerPolicy policy = createPolicyAddResources(metadataObject); + + ownerPrivileges.forEach( + ownerPrivilege -> { + // Each owner's privilege will create one RangerPolicyItemAccess in the policy + RangerPolicy.RangerPolicyItem policyItem = new RangerPolicy.RangerPolicyItem(); + policyItem + .getAccesses() + .add(new RangerPolicy.RangerPolicyItemAccess(ownerPrivilege.getName())); + policyItem.getRoles().add(ownerRoleName); + policy.getPolicyItems().add(policyItem); + }); + return policy; + } + + protected void updatePolicyOwnerRole(RangerPolicy policy, String ownerRoleName) { + // Find matching policy items based on the owner's privileges + List matchPolicyItems = + policy.getPolicyItems().stream() + .filter( + policyItem -> { + return policyItem.getAccesses().stream() + .allMatch( + policyItemAccess -> { + return ownerPrivileges.stream() + .anyMatch( + ownerPrivilege -> { + return ownerPrivilege.equalsTo(policyItemAccess.getType()); + }); + }); + }) + .collect(Collectors.toList()); + // Add or remove the owner role in the policy item + matchPolicyItems.forEach( + policyItem -> { + if (!policyItem.getRoles().contains(ownerRoleName)) { + policyItem.getRoles().add(ownerRoleName); + } + }); + + // If the policy item is not equal to owner's privileges, then update the policy + ownerPrivileges.stream() + .filter( + ownerPrivilege -> { + return matchPolicyItems.stream() + .noneMatch( + policyItem -> { + return policyItem.getAccesses().stream() + .anyMatch( + policyItemAccess -> { + return ownerPrivilege.equalsTo(policyItemAccess.getType()); + }); + }); + }) + .forEach( + // Add lost owner's privilege to the policy + ownerPrivilege -> { + RangerPolicy.RangerPolicyItem policyItem = new RangerPolicy.RangerPolicyItem(); + policyItem + .getAccesses() + .add(new RangerPolicy.RangerPolicyItemAccess(ownerPrivilege.getName())); + if (!policyItem.getRoles().contains(ownerRoleName)) { + policyItem.getRoles().add(ownerRoleName); + } + policy.getPolicyItems().add(policyItem); + }); } } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilege.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilege.java index 0953ac9a518..04774b417e1 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilege.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilege.java @@ -18,64 +18,13 @@ */ package org.apache.gravitino.authorization.ranger; +import org.apache.gravitino.authorization.Privilege; + /** RangerPrivilege interface is used to define the Ranger privileges. */ public interface RangerPrivilege { String getName(); - boolean equalsTo(String value); - - /** Ranger Hive privileges enumeration. */ - enum RangerHivePrivilege implements RangerPrivilege { - ALL("all"), - SELECT("select"), - UPDATE("update"), - CREATE("create"), - DROP("drop"), - ALTER("alter"), - INDEX("index"), - LOCK("lock"), - READ("read"), - WRITE("write"), - REPLADMIN("repladmin"), - SERVICEADMIN("serviceadmin"); - - private final String name; // Access a type in the Ranger policy item - - RangerHivePrivilege(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } + Privilege.Condition condition(); - @Override - public boolean equalsTo(String value) { - return name.equalsIgnoreCase(value); - } - } - - /** Ranger HDFS privileges enumeration. */ - enum RangerHdfsPrivilege implements RangerPrivilege { - READ("read"), - WRITE("write"), - EXECUTE("execute"); - - private final String name; // Access a type in the Ranger policy item - - RangerHdfsPrivilege(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean equalsTo(String value) { - return name.equalsIgnoreCase(value); - } - } + boolean equalsTo(String value); } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivileges.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivileges.java index 1dff01dc8f3..c547781678c 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivileges.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivileges.java @@ -18,18 +18,111 @@ */ package org.apache.gravitino.authorization.ranger; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import java.util.List; +import org.apache.gravitino.authorization.Privilege; public class RangerPrivileges { + /** Ranger Hive privileges enumeration. */ + public enum RangerHivePrivilege implements RangerPrivilege { + ALL("all"), + SELECT("select"), + UPDATE("update"), + CREATE("create"), + DROP("drop"), + ALTER("alter"), + INDEX("index"), + LOCK("lock"), + READ("read"), + WRITE("write"), + REPLADMIN("repladmin"), + SERVICEADMIN("serviceadmin"); + + private final String name; // Access a type in the Ranger policy item + + RangerHivePrivilege(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Privilege.Condition condition() { + return null; + } + + @Override + public boolean equalsTo(String value) { + return name.equalsIgnoreCase(value); + } + } + + public static class RangerHivePrivilegeImpl implements RangerPrivilege { + private RangerPrivilege rangerHivePrivilege; + private Privilege.Condition condition; + + public RangerHivePrivilegeImpl( + RangerPrivilege rangerHivePrivilege, Privilege.Condition condition) { + this.rangerHivePrivilege = rangerHivePrivilege; + this.condition = condition; + } + + @Override + public String getName() { + return rangerHivePrivilege.getName(); + } + + @Override + public Privilege.Condition condition() { + return condition; + } + + @Override + public boolean equalsTo(String value) { + return rangerHivePrivilege.equalsTo(value); + } + } + + /** Ranger HDFS privileges enumeration. */ + public enum RangerHdfsPrivilege implements RangerPrivilege { + READ("read"), + WRITE("write"), + EXECUTE("execute"); + + private final String name; // Access a type in the Ranger policy item + + RangerHdfsPrivilege(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Privilege.Condition condition() { + return null; + } + + @Override + public boolean equalsTo(String value) { + return name.equalsIgnoreCase(value); + } + } + static List>> allRangerPrivileges = Lists.newArrayList( - RangerPrivilege.RangerHivePrivilege.class, RangerPrivilege.RangerHdfsPrivilege.class); + RangerPrivileges.RangerHivePrivilege.class, RangerPrivileges.RangerHdfsPrivilege.class); - public static RangerPrivilege valueOf(String string) { - RangerHelper.check(string != null, "Privilege name string cannot be null!"); + public static RangerPrivilege valueOf(String name) { + Preconditions.checkArgument(name != null, "Privilege name string cannot be null!"); - String strPrivilege = string.trim().toLowerCase(); + String strPrivilege = name.trim().toLowerCase(); for (Class> enumClass : allRangerPrivileges) { for (Enum privilege : enumClass.getEnumConstants()) { if (((RangerPrivilege) privilege).equalsTo(strPrivilege)) { @@ -37,6 +130,6 @@ public static RangerPrivilege valueOf(String string) { } } } - throw new IllegalArgumentException("Unknown privilege string: " + string); + throw new IllegalArgumentException("Unknown privilege name: " + name); } } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilegesMappingProvider.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilegesMappingProvider.java index c6a154d2218..6b30b4bb1c6 100644 --- a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilegesMappingProvider.java +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerPrivilegesMappingProvider.java @@ -21,7 +21,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.gravitino.MetadataObject; import org.apache.gravitino.authorization.Privilege; +import org.apache.gravitino.authorization.SecurableObject; /** * Ranger authorization use this provider to mapping Gravitino privilege to the Ranger privileges. @@ -29,12 +31,47 @@ * HBase, etc. */ public interface RangerPrivilegesMappingProvider { - /** Set the mapping Gravitino privilege name to the Ranger privileges rule. */ + /** + * Set the mapping Gravitino privilege name to the Ranger privileges rule. + * + * @return The mapping Gravitino privilege name to the Ranger privileges rule. + */ Map> privilegesMappingRule(); - /** Set the owner privileges rule. */ + /** + * Set the owner Ranger privileges rule. + * + * @return The owner Ranger privileges rule. + */ Set ownerMappingRule(); - /** Set the policy resource defines rule. */ + /** + * Set the Ranger policy resource defines rule. + * + * @return The policy resource defines rule. + */ List policyResourceDefinesRule(); + + /** + * Allow Gravitino privilege operation defines rule. + * + * @return The allow Gravitino privilege operation defines rule. + */ + Set allowPrivilegesRule(); + + /** + * Translate the Gravitino securable object to the Ranger securable object. + * + * @param securableObject The Gravitino securable object. + * @return The Ranger securable object list. + */ + List translatePrivilege(SecurableObject securableObject); + + /** + * Translate the Gravitino securable object to the Ranger owner securable object. + * + * @param metadataObject The Gravitino metadata object. + * @return The Ranger owner securable object list. + */ + List translateOwner(MetadataObject metadataObject); } diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObject.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObject.java new file mode 100644 index 00000000000..3cdc4e514f5 --- /dev/null +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObject.java @@ -0,0 +1,42 @@ +/* + * 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.authorization.ranger; + +import java.util.List; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.annotation.Unstable; + +/** + * The Ranger securable object is the entity which access can be granted. Unless allowed by a grant, + * access is denied.
+ * You can use the helper class `RangerSecurableObjects` to create the Ranger securable object which + * you need.
+ * There is a clear difference between Ranger's Securable Object and Gravitino's Securable Object, + * Ranger's Securable Object does not have the concept of `METALAKE`, so it needs to be defined + * specifically. + */ +@Unstable +public interface RangerSecurableObject extends MetadataObject { + /** + * The privileges of the Ranger securable object. + * + * @return The privileges of the securable object. + */ + List privileges(); +} diff --git a/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObjects.java b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObjects.java new file mode 100644 index 00000000000..6405e3e4c7d --- /dev/null +++ b/authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerSecurableObjects.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.authorization.ranger; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import java.util.List; +import java.util.Set; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.MetadataObjects; +import org.apache.gravitino.MetadataObjects.MetadataObjectImpl; + +/** The helper class for {@link RangerSecurableObject}. */ +public class RangerSecurableObjects { + public static RangerSecurableObject of( + List names, MetadataObject.Type type, Set privileges) { + MetadataObject metadataObject = + MetadataObjects.of( + MetadataObjects.getParentFullName(names), names.get(names.size() - 1), type); + return new RangerSecurableObjectImpl( + metadataObject.parent(), metadataObject.name(), type, privileges); + } + + private static class RangerSecurableObjectImpl extends MetadataObjectImpl + implements RangerSecurableObject { + private final List privileges; + + /** + * Create the Ranger securable object with the given name, parent and type. + * + * @param parent The parent of the metadata object + * @param name The name of the metadata object + * @param type The type of the metadata object + */ + public RangerSecurableObjectImpl( + String parent, String name, Type type, Set privileges) { + super(parent, name, type); + this.privileges = ImmutableList.copyOf(Sets.newHashSet(privileges)); + } + + @Override + public List privileges() { + return privileges; + } + } +} diff --git a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/TestRangerAuthorizationPlugin.java b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/TestRangerAuthorizationPlugin.java new file mode 100644 index 00000000000..d4f314dd7b7 --- /dev/null +++ b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/TestRangerAuthorizationPlugin.java @@ -0,0 +1,666 @@ +/* + * 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.authorization.ranger; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.List; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.MetadataObjects; +import org.apache.gravitino.authorization.Privilege; +import org.apache.gravitino.authorization.Privileges; +import org.apache.gravitino.authorization.SecurableObject; +import org.apache.gravitino.authorization.SecurableObjects; +import org.apache.gravitino.authorization.ranger.integration.test.RangerITEnv; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TestRangerAuthorizationPlugin { + private static RangerAuthorizationPlugin rangerAuthPlugin; + + @BeforeAll + public static void setup() { + RangerITEnv.init(); + rangerAuthPlugin = RangerITEnv.rangerAuthHivePlugin; + } + + @Test + public void testTranslatePrivilege() { + SecurableObject createSchemaInMetalake = + SecurableObjects.parse( + String.format("metalake1"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateSchema.allow())); + List createSchemaInMetalake1 = + rangerAuthPlugin.translatePrivilege(createSchemaInMetalake); + Assertions.assertEquals(1, createSchemaInMetalake1.size()); + Assertions.assertEquals("*", createSchemaInMetalake1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.SCHEMA, createSchemaInMetalake1.get(0).type()); + + SecurableObject createSchemaInCatalog = + SecurableObjects.parse( + String.format("catalog1"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateSchema.allow())); + List createSchemaInCatalog1 = + rangerAuthPlugin.translatePrivilege(createSchemaInCatalog); + Assertions.assertEquals(1, createSchemaInCatalog1.size()); + Assertions.assertEquals("*", createSchemaInCatalog1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.SCHEMA, createSchemaInCatalog1.get(0).type()); + + for (Privilege privilege : + ImmutableList.of( + Privileges.CreateTable.allow(), + Privileges.ModifyTable.allow(), + Privileges.SelectTable.allow())) { + SecurableObject metalake = + SecurableObjects.parse( + String.format("metalake1"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateTable.allow())); + List metalake1 = rangerAuthPlugin.translatePrivilege(metalake); + Assertions.assertEquals(2, metalake1.size()); + Assertions.assertEquals("*.*", metalake1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, metalake1.get(0).type()); + Assertions.assertEquals("*.*.*", metalake1.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, metalake1.get(1).type()); + + SecurableObject catalog = + SecurableObjects.parse( + String.format("catalog1"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateTable.allow())); + List catalog1 = rangerAuthPlugin.translatePrivilege(catalog); + Assertions.assertEquals(2, catalog1.size()); + Assertions.assertEquals("*.*", catalog1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, catalog1.get(0).type()); + Assertions.assertEquals("*.*.*", catalog1.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, catalog1.get(1).type()); + + SecurableObject schema = + SecurableObjects.parse( + String.format("catalog1.schema1"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(privilege)); + List schema1 = rangerAuthPlugin.translatePrivilege(schema); + Assertions.assertEquals(2, schema1.size()); + Assertions.assertEquals("schema1.*", schema1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, schema1.get(0).type()); + Assertions.assertEquals("schema1.*.*", schema1.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, schema1.get(1).type()); + + if (!privilege.equals(Privileges.CreateTable.allow())) { + // `CREATE_TABLE` not support securable object for table, So ignore check for table. + SecurableObject table = + SecurableObjects.parse( + String.format("catalog1.schema1.table1"), + MetadataObject.Type.TABLE, + Lists.newArrayList(privilege)); + List table1 = rangerAuthPlugin.translatePrivilege(table); + Assertions.assertEquals(2, table1.size()); + Assertions.assertEquals("schema1.table1", table1.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, table1.get(0).type()); + Assertions.assertEquals("schema1.table1.*", table1.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, table1.get(1).type()); + } + } + } + + @Test + public void testTranslateOwner() { + for (MetadataObject.Type type : + ImmutableList.of(MetadataObject.Type.METALAKE, MetadataObject.Type.CATALOG)) { + MetadataObject metalake = MetadataObjects.parse("metalake_or_catalog", type); + List metalakeOwner = rangerAuthPlugin.translateOwner(metalake); + Assertions.assertEquals(3, metalakeOwner.size()); + Assertions.assertEquals("*", metalakeOwner.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.SCHEMA, metalakeOwner.get(0).type()); + Assertions.assertEquals("*.*", metalakeOwner.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, metalakeOwner.get(1).type()); + Assertions.assertEquals("*.*.*", metalakeOwner.get(2).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, metalakeOwner.get(2).type()); + } + + MetadataObject schema = MetadataObjects.parse("catalog1.schema1", MetadataObject.Type.SCHEMA); + List schemaOwner = rangerAuthPlugin.translateOwner(schema); + Assertions.assertEquals(3, schemaOwner.size()); + Assertions.assertEquals("schema1", schemaOwner.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.SCHEMA, schemaOwner.get(0).type()); + Assertions.assertEquals("schema1.*", schemaOwner.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, schemaOwner.get(1).type()); + Assertions.assertEquals("schema1.*.*", schemaOwner.get(2).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, schemaOwner.get(2).type()); + + MetadataObject table = + MetadataObjects.parse("catalog1.schema1.table1", MetadataObject.Type.TABLE); + List tableOwner = rangerAuthPlugin.translateOwner(table); + Assertions.assertEquals(2, tableOwner.size()); + Assertions.assertEquals("schema1.table1", tableOwner.get(0).fullName()); + Assertions.assertEquals(MetadataObject.Type.TABLE, tableOwner.get(0).type()); + Assertions.assertEquals("schema1.table1.*", tableOwner.get(1).fullName()); + Assertions.assertEquals(MetadataObject.Type.COLUMN, tableOwner.get(1).type()); + } + + @Test + public void testValidAuthorizationOperation() { + // Create Catalog + SecurableObject createCatalog = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateCatalog.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createCatalog))); + + // Use catalog operation + SecurableObject useCatalogInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.UseCatalog.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useCatalogInMetalake))); + SecurableObject useCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.UseCatalog.allow())); + Assertions.assertTrue(rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useCatalog))); + + // Create schema + SecurableObject createSchemaInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateSchema.allow())); + SecurableObject createSchemaInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateSchema.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation( + Arrays.asList(createSchemaInMetalake, createSchemaInCatalog))); + + // Use schema operation + SecurableObject useSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.UseSchema.allow())); + Assertions.assertTrue(rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useSchema))); + + // Table + SecurableObject createTableInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateTable.allow())); + SecurableObject createTableInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateTable.allow())); + SecurableObject createTableInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.CreateTable.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation( + Arrays.asList(createTableInMetalake, createTableInCatalog, createTableInSchema))); + + SecurableObject modifyTableInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.ModifyTable.allow())); + SecurableObject modifyTableInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.ModifyTable.allow())); + SecurableObject modifyTableInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.ModifyTable.allow())); + SecurableObject modifyTable = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.ModifyTable.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation( + Arrays.asList( + modifyTableInMetalake, modifyTableInCatalog, modifyTableInSchema, modifyTable))); + + SecurableObject selectTableInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.SelectTable.allow())); + SecurableObject selectTableInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.SelectTable.allow())); + SecurableObject selectTableInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.SelectTable.allow())); + SecurableObject selectTable = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.SelectTable.allow())); + Assertions.assertTrue( + rangerAuthPlugin.validAuthorizationOperation( + Arrays.asList( + selectTableInMetalake, selectTableInCatalog, selectTableInSchema, selectTable))); + + // Ignore the Fileset operation + SecurableObject createFilesetInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateFileset.allow())); + SecurableObject createFilesetInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateFileset.allow())); + SecurableObject createFilesetInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.ReadFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFilesetInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFilesetInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFilesetInSchema))); + + // Ignore the Topic operation + SecurableObject writeFilesetInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.WriteFileset.allow())); + SecurableObject writeFilesetInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.WriteFileset.allow())); + SecurableObject writeFilesetInScheam = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.WriteFileset.allow())); + SecurableObject writeFileset = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.WriteFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFilesetInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFilesetInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFilesetInScheam))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFileset))); + + // Ignore the Fileset operation + SecurableObject readFilesetInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.ReadFileset.allow())); + SecurableObject readFilesetInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.ReadFileset.allow())); + SecurableObject readFilesetInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.ReadFileset.allow())); + SecurableObject readFileset = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.ReadFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFilesetInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFilesetInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFilesetInSchema))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFileset))); + + // Ignore the Topic operation + SecurableObject createTopicInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateTopic.allow())); + SecurableObject createTopicInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateTopic.allow())); + SecurableObject createTopicInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopicInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopicInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopicInSchema))); + + SecurableObject produceTopicInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + SecurableObject produceTopicInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + SecurableObject produceTopicInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + SecurableObject produceTopic = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopicInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopicInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopicInSchema))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopic))); + + SecurableObject consumeTopicInMetalake = + SecurableObjects.parse( + String.format("metalake"), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + SecurableObject consumeTopicInCatalog = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + SecurableObject consumeTopicInSchema = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + SecurableObject consumeTopic = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopicInMetalake))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopicInCatalog))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopicInSchema))); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopic))); + } + + @Test + public void testInvalidAuthorizationOperation() { + // Catalog + SecurableObject createCatalog1 = + SecurableObjects.parse( + String.format("catalog"), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.CreateCatalog.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createCatalog1))); + SecurableObject createCatalog2 = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.CreateCatalog.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createCatalog2))); + SecurableObject createCatalog3 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.CreateCatalog.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createCatalog3))); + + SecurableObject useCatalog1 = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.UseCatalog.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useCatalog1))); + SecurableObject useCatalog2 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.UseCatalog.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useCatalog2))); + + // Schema + SecurableObject createSchema1 = + SecurableObjects.parse( + String.format("catalog.schema"), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.CreateSchema.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createSchema1))); + SecurableObject createSchema2 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.CreateSchema.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createSchema2))); + + SecurableObject useSchema1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.UseSchema.allow())); + Assertions.assertFalse(rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(useSchema1))); + + // Table + SecurableObject createTable1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.CreateTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTable1))); + SecurableObject createTable2 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.CreateTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTable2))); + SecurableObject createTable3 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.CreateTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTable3))); + + SecurableObject modifyTable1 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.ModifyTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(modifyTable1))); + SecurableObject modifyTable2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.ModifyTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(modifyTable2))); + + SecurableObject selectTable1 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.SelectTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(selectTable1))); + SecurableObject selectTable2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.SelectTable.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(selectTable2))); + + // Topic + SecurableObject createTopic1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopic1))); + SecurableObject createTopic2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopic2))); + SecurableObject createTopic3 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createTopic3))); + SecurableObject produceTopic1 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopic1))); + SecurableObject produceTopic2 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.ProduceTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(produceTopic2))); + + SecurableObject consumeTopic1 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopic1))); + SecurableObject consumeTopic2 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.ConsumeTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(consumeTopic2))); + + // Fileset + SecurableObject createFileset1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.CreateFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFileset1))); + SecurableObject createFileset2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFileset2))); + SecurableObject createFileset3 = + SecurableObjects.parse( + String.format("catalog.schema.fileset"), + MetadataObject.Type.FILESET, + Lists.newArrayList(Privileges.CreateTopic.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(createFileset3))); + SecurableObject writeFileset1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.WriteFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFileset1))); + SecurableObject writeFileset2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.WriteFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(writeFileset2))); + + SecurableObject readFileset1 = + SecurableObjects.parse( + String.format("catalog.schema.table"), + MetadataObject.Type.TABLE, + Lists.newArrayList(Privileges.ReadFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFileset1))); + SecurableObject readFileset2 = + SecurableObjects.parse( + String.format("catalog.schema.topic"), + MetadataObject.Type.TOPIC, + Lists.newArrayList(Privileges.ReadFileset.allow())); + Assertions.assertFalse( + rangerAuthPlugin.validAuthorizationOperation(Arrays.asList(readFileset2))); + } +} diff --git a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java index 0f1f4c05abd..208e3d64e65 100644 --- a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java +++ b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveE2EIT.java @@ -47,8 +47,6 @@ import org.apache.gravitino.authorization.Privileges; import org.apache.gravitino.authorization.SecurableObject; import org.apache.gravitino.authorization.SecurableObjects; -import org.apache.gravitino.authorization.ranger.RangerAuthorizationHivePlugin; -import org.apache.gravitino.authorization.ranger.RangerAuthorizationPlugin; import org.apache.gravitino.catalog.hive.HiveConstants; import org.apache.gravitino.client.GravitinoMetalake; import org.apache.gravitino.connector.AuthorizationPropertiesMeta; @@ -74,7 +72,6 @@ public class RangerHiveE2EIT extends BaseIT { private static final Logger LOG = LoggerFactory.getLogger(RangerHiveE2EIT.class); - private static RangerAuthorizationPlugin rangerAuthPlugin; public static final String metalakeName = GravitinoITUtils.genRandomName("RangerHiveE2EIT_metalake").toLowerCase(); public static final String catalogName = @@ -109,7 +106,7 @@ public void startIntegrationTest() throws Exception { registerCustomConfigs(configs); super.startIntegrationTest(); - RangerITEnv.setup(); + RangerITEnv.init(); RangerITEnv.startHiveRangerContainer(); RANGER_ADMIN_URL = @@ -145,7 +142,7 @@ public void startIntegrationTest() throws Exception { .getOrCreate(); createMetalake(); - createCatalogAndRangerAuthPlugin(); + createCatalog(); } private static void generateRangerSparkSecurityXML() throws IOException { @@ -180,8 +177,8 @@ private static void generateRangerSparkSecurityXML() throws IOException { } @AfterAll - public void stop() throws IOException { - client = null; + public void stop() { + RangerITEnv.cleanup(); if (client != null) { Arrays.stream(catalog.asSchemas().listSchemas()) .filter(schema -> !schema.equals("default")) @@ -221,8 +218,8 @@ void testAllowUseSchemaPrivilege() throws InterruptedException { // Create a role with CREATE_SCHEMA privilege SecurableObject securableObject1 = SecurableObjects.parse( - String.format("%s.%s", catalogName, schemaName), - MetadataObject.Type.SCHEMA, + String.format("%s", catalogName), + MetadataObject.Type.CATALOG, Lists.newArrayList(Privileges.CreateSchema.allow())); RoleEntity role = RoleEntity.builder() @@ -231,7 +228,7 @@ void testAllowUseSchemaPrivilege() throws InterruptedException { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(securableObject1)) .build(); - rangerAuthPlugin.onRoleCreated(role); + RangerITEnv.rangerAuthHivePlugin.onRoleCreated(role); // Granted this role to the spark execution user `HADOOP_USER_NAME` String userName1 = System.getenv(HADOOP_USER_NAME); @@ -244,7 +241,8 @@ void testAllowUseSchemaPrivilege() throws InterruptedException { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); + RangerITEnv.rangerAuthHivePlugin.onGrantedRolesToUser( + Lists.newArrayList(role), userEntity1)); // After Ranger Authorization, Must wait a period of time for the Ranger Spark plugin to update // the policy Sleep time must be greater than the policy update interval @@ -277,21 +275,7 @@ private void createMetalake() { metalake = loadMetalake; } - private static void createCatalogAndRangerAuthPlugin() { - rangerAuthPlugin = - RangerAuthorizationHivePlugin.getInstance( - ImmutableMap.of( - AuthorizationPropertiesMeta.RANGER_ADMIN_URL, - RANGER_ADMIN_URL, - RANGER_AUTH_TYPE, - RangerContainer.authType, - RANGER_USERNAME, - RangerContainer.rangerUserName, - RANGER_PASSWORD, - RangerContainer.rangerPassword, - RANGER_SERVICE_NAME, - RangerITEnv.RANGER_HIVE_REPO_NAME)); - + private static void createCatalog() { Map properties = ImmutableMap.of( HiveConstants.METASTORE_URIS, diff --git a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveIT.java b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveIT.java index 97005e58c15..eba00a18864 100644 --- a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveIT.java +++ b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerHiveIT.java @@ -20,10 +20,10 @@ import static org.apache.gravitino.authorization.SecurableObjects.DOT_SPLITTER; import static org.apache.gravitino.authorization.ranger.integration.test.RangerITEnv.currentFunName; -import static org.apache.gravitino.authorization.ranger.integration.test.RangerITEnv.rangerClient; import static org.apache.gravitino.authorization.ranger.integration.test.RangerITEnv.verifyRoleInRanger; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import java.time.Instant; import java.util.ArrayList; @@ -35,21 +35,18 @@ import org.apache.gravitino.MetadataObject; import org.apache.gravitino.MetadataObjects; import org.apache.gravitino.authorization.Owner; +import org.apache.gravitino.authorization.Privilege; import org.apache.gravitino.authorization.Privileges; import org.apache.gravitino.authorization.Role; import org.apache.gravitino.authorization.RoleChange; import org.apache.gravitino.authorization.SecurableObject; import org.apache.gravitino.authorization.SecurableObjects; -import org.apache.gravitino.authorization.ranger.RangerAuthorizationHivePlugin; import org.apache.gravitino.authorization.ranger.RangerAuthorizationPlugin; import org.apache.gravitino.authorization.ranger.RangerHelper; -import org.apache.gravitino.authorization.ranger.RangerPrivilege; import org.apache.gravitino.authorization.ranger.RangerPrivileges; +import org.apache.gravitino.authorization.ranger.RangerSecurableObject; +import org.apache.gravitino.authorization.ranger.RangerSecurableObjects; import org.apache.gravitino.authorization.ranger.reference.RangerDefines; -import org.apache.gravitino.connector.AuthorizationPropertiesMeta; -import org.apache.gravitino.integration.test.container.ContainerSuite; -import org.apache.gravitino.integration.test.container.HiveContainer; -import org.apache.gravitino.integration.test.container.RangerContainer; import org.apache.gravitino.integration.test.util.GravitinoITUtils; import org.apache.gravitino.meta.AuditInfo; import org.apache.gravitino.meta.GroupEntity; @@ -57,6 +54,8 @@ import org.apache.gravitino.meta.UserEntity; import org.apache.ranger.RangerServiceException; import org.apache.ranger.plugin.model.RangerPolicy; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -68,58 +67,46 @@ public class RangerHiveIT { private static final Logger LOG = LoggerFactory.getLogger(RangerHiveIT.class); - private static final ContainerSuite containerSuite = ContainerSuite.getInstance(); - private static final String adminUser = "gravitino"; - private static RangerAuthorizationPlugin rangerAuthPlugin; + private static RangerAuthorizationPlugin rangerAuthHivePlugin; private static RangerHelper rangerHelper; private final AuditInfo auditInfo = AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build(); @BeforeAll public static void setup() { - RangerITEnv.setup(); - - containerSuite.startHiveRangerContainer( - new HashMap<>( - ImmutableMap.of( - HiveContainer.HIVE_RUNTIME_VERSION, - HiveContainer.HIVE3, - RangerContainer.DOCKER_ENV_RANGER_SERVER_URL, - String.format( - "http://%s:%d", - containerSuite.getRangerContainer().getContainerIpAddress(), - RangerContainer.RANGER_SERVER_PORT), - RangerContainer.DOCKER_ENV_RANGER_HIVE_REPOSITORY_NAME, - RangerITEnv.RANGER_HIVE_REPO_NAME, - RangerContainer.DOCKER_ENV_RANGER_HDFS_REPOSITORY_NAME, - RangerITEnv.RANGER_HDFS_REPO_NAME, - HiveContainer.HADOOP_USER_NAME, - adminUser))); - - rangerAuthPlugin = - RangerAuthorizationHivePlugin.getInstance( - ImmutableMap.of( - AuthorizationPropertiesMeta.RANGER_ADMIN_URL, - String.format( - "http://%s:%d", - containerSuite.getRangerContainer().getContainerIpAddress(), - RangerContainer.RANGER_SERVER_PORT), - AuthorizationPropertiesMeta.RANGER_AUTH_TYPE, - RangerContainer.authType, - AuthorizationPropertiesMeta.RANGER_USERNAME, - RangerContainer.rangerUserName, - AuthorizationPropertiesMeta.RANGER_PASSWORD, - RangerContainer.rangerPassword, - AuthorizationPropertiesMeta.RANGER_SERVICE_NAME, - RangerITEnv.RANGER_HIVE_REPO_NAME)); - rangerHelper = - new RangerHelper( - rangerClient, - RangerContainer.rangerUserName, - RangerITEnv.RANGER_HIVE_REPO_NAME, - rangerAuthPlugin.privilegesMappingRule(), - rangerAuthPlugin.ownerMappingRule(), - rangerAuthPlugin.policyResourceDefinesRule()); + RangerITEnv.init(); + + rangerAuthHivePlugin = RangerITEnv.rangerAuthHivePlugin; + rangerHelper = RangerITEnv.rangerHelper; + } + + @AfterAll + public static void stop() { + RangerITEnv.cleanup(); + } + + @AfterEach + public void clean() { + // Clean up the test Ranger policy + Role mockCatalogRole = mockCatalogRole(currentFunName()); + mockCatalogRole.securableObjects().stream() + .forEach( + securableObject -> { + rangerAuthHivePlugin.translatePrivilege(securableObject).stream() + .forEach( + rangerSecurableObject -> { + deleteHivePolicy(rangerSecurableObject); + }); + }); + mockCatalogRole.securableObjects().stream() + .forEach( + securableObject -> { + rangerAuthHivePlugin.translateOwner(securableObject).stream() + .forEach( + rangerSecurableObject -> { + deleteHivePolicy(rangerSecurableObject); + }); + }); } /** @@ -133,8 +120,8 @@ public static void setup() { public RoleEntity mock3TableRole(String roleName) { SecurableObject securableObject1 = SecurableObjects.parse( - String.format("catalog.%s.tab1", roleName), // use unique db name to avoid conflict - MetadataObject.Type.TABLE, + String.format("catalog.%s", roleName), // use unique db name to avoid conflict + MetadataObject.Type.SCHEMA, Lists.newArrayList(Privileges.CreateTable.allow())); SecurableObject securableObject2 = @@ -162,23 +149,42 @@ public RoleEntity mock3TableRole(String roleName) { @Test public void testOnRoleCreated() { RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); - verifyRoleInRanger(rangerAuthPlugin, role); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); + verifyRoleInRanger(rangerAuthHivePlugin, role); + } + + @Test + public void testOnRoleCreatedCatalog() { + Role mockCatalogRole = mockCatalogRole(currentFunName()); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(mockCatalogRole)); + // Check if exist this policy + assertFindManagedPolicy(mockCatalogRole, true); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(mockCatalogRole)); + assertFindManagedPolicy(mockCatalogRole, false); } @Test public void testOnRoleDeleted() { // prepare to create a role RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); + assertFindManagedPolicy(role, true); // delete this role - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(role)); // Check if the policy is deleted - role.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNull(rangerHelper.findManagedPolicy(securableObject))); + assertFindManagedPolicy(role, false); + } + + @Test + public void testOnRoleDeletedCatalog() { + // prepare to create a role + Role mockCatalogRole = mockCatalogRole(currentFunName()); + + // delete this role + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(mockCatalogRole)); + // Check if exist this policy + assertFindManagedPolicy(mockCatalogRole, false); } @Test @@ -190,24 +196,21 @@ public void testOnRoleDeleted2() { .forEach( securableObject -> { Assertions.assertTrue( - rangerAuthPlugin.onOwnerSet( + rangerAuthHivePlugin.onOwnerSet( securableObject, null, new MockOwner("user1", Owner.Type.USER))); }); // delete this role - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(role)); // Because this metaobject has owner, so the policy should not be deleted - role.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject))); + assertFindManagedPolicy(role, true); } @Test public void testOnRoleAcquired() { RoleEntity role = mock3TableRole(GravitinoITUtils.genRandomName(currentFunName())); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); - Assertions.assertTrue(rangerAuthPlugin.onRoleAcquired(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleAcquired(role)); } /** The metalake role does not to create Ranger policy. Only use it to help test */ @@ -216,7 +219,7 @@ public RoleEntity mockCatalogRole(String roleName) { SecurableObjects.parse( "catalog", SecurableObject.Type.CATALOG, - Lists.newArrayList(Privileges.UseCatalog.allow())); + Lists.newArrayList(Privileges.CreateSchema.allow())); RoleEntity role = RoleEntity.builder() .withId(1L) @@ -245,19 +248,21 @@ public void testFindManagedPolicy() { Lists.newArrayList(String.format("%s3", dbName), "tab*"), GravitinoITUtils.genRandomName(currentFunName())); // findManagedPolicy function use precise search, so return null - SecurableObject securableObject1 = - SecurableObjects.parse( - String.format("catalog.%s3.tab1", dbName), + RangerSecurableObject rangerSecurableObject = + RangerSecurableObjects.of( + ImmutableList.of(String.format("%s3", dbName), "tab1"), MetadataObject.Type.TABLE, - Lists.newArrayList(Privileges.CreateTable.allow())); - Assertions.assertNull(rangerHelper.findManagedPolicy(securableObject1)); + ImmutableSet.of( + new RangerPrivileges.RangerHivePrivilegeImpl( + RangerPrivileges.RangerHivePrivilege.ALL, Privilege.Condition.ALLOW))); + Assertions.assertNull(rangerHelper.findManagedPolicy(rangerSecurableObject)); // Add a policy for `db3.tab1` createHivePolicy( Lists.newArrayList(String.format("%s3", dbName), "tab1"), GravitinoITUtils.genRandomName(currentFunName())); // findManagedPolicy function use precise search, so return not null - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject1)); + Assertions.assertNotNull(rangerHelper.findManagedPolicy(rangerSecurableObject)); } static void createHivePolicy(List metaObjects, String roleName) { @@ -278,7 +283,7 @@ static void createHivePolicy(List metaObjects, String roleName) { policyItem.setAccesses( Arrays.asList( new RangerPolicy.RangerPolicyItemAccess( - RangerPrivilege.RangerHivePrivilege.SELECT.toString()))); + RangerPrivileges.RangerHivePrivilege.SELECT.toString()))); RangerITEnv.updateOrCreateRangerPolicy( RangerDefines.SERVICE_TYPE_HIVE, RangerITEnv.RANGER_HIVE_REPO_NAME, @@ -287,18 +292,30 @@ static void createHivePolicy(List metaObjects, String roleName) { Collections.singletonList(policyItem)); } + static boolean deleteHivePolicy(RangerSecurableObject rangerSecurableObject) { + RangerPolicy policy = rangerHelper.findManagedPolicy(rangerSecurableObject); + if (policy != null) { + try { + RangerITEnv.rangerClient.deletePolicy(policy.getId()); + } catch (RangerServiceException e) { + return false; + } + } + return true; + } + @Test public void testRoleChangeAddSecurableObject() { SecurableObject securableObject1 = SecurableObjects.parse( String.format("catalog.%s.tab1", currentFunName()), SecurableObject.Type.TABLE, - Lists.newArrayList(Privileges.CreateTable.allow())); + Lists.newArrayList(Privileges.SelectTable.allow())); Role mockCatalogRole = mockCatalogRole(currentFunName()); // 1. Add a securable object to the role Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( + rangerAuthHivePlugin.onRoleUpdated( mockCatalogRole, RoleChange.addSecurableObject(mockCatalogRole.name(), securableObject1))); @@ -310,15 +327,15 @@ public void testRoleChangeAddSecurableObject() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(securableObject1)) .build(); - verifyRoleInRanger(rangerAuthPlugin, verifyRole1); + verifyRoleInRanger(rangerAuthHivePlugin, verifyRole1); // 2. Multi-call Add a same entity and privilege to the role, because support idempotent // operation, so return true Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( + rangerAuthHivePlugin.onRoleUpdated( mockCatalogRole, RoleChange.addSecurableObject(mockCatalogRole.name(), securableObject1))); - verifyRoleInRanger(rangerAuthPlugin, verifyRole1); + verifyRoleInRanger(rangerAuthHivePlugin, verifyRole1); // 3. Add the same metadata but have different privileges to the role SecurableObject securableObject3 = @@ -327,17 +344,28 @@ public void testRoleChangeAddSecurableObject() { SecurableObject.Type.TABLE, Lists.newArrayList(Privileges.SelectTable.allow(), Privileges.ModifyTable.allow())); Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( + rangerAuthHivePlugin.onRoleUpdated( mockCatalogRole, RoleChange.addSecurableObject(mockCatalogRole.name(), securableObject3))); } + @Test + public void testRoleChangeAddCatalogSecurableObject() { + Role mockCatalogRole = mockCatalogRole(currentFunName()); + Assertions.assertTrue( + rangerAuthHivePlugin.onRoleUpdated( + mockCatalogRole, + RoleChange.addSecurableObject( + mockCatalogRole.name(), mockCatalogRole.securableObjects().get(0)))); + assertFindManagedPolicy(mockCatalogRole, true); + } + @Test public void testRoleChangeRemoveSecurableObject() { // Prepare a role contain 3 securable objects String currentFunName = currentFunName(); RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); Role mockCatalogRole = mockCatalogRole(currentFunName); // remove a securable object from role @@ -345,7 +373,7 @@ public void testRoleChangeRemoveSecurableObject() { while (!securableObjects.isEmpty()) { SecurableObject removeSecurableObject = securableObjects.remove(0); Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( + rangerAuthHivePlugin.onRoleUpdated( mockCatalogRole, RoleChange.removeSecurableObject(mockCatalogRole.name(), removeSecurableObject))); @@ -358,17 +386,30 @@ public void testRoleChangeRemoveSecurableObject() { .withAuditInfo(auditInfo) .withSecurableObjects(securableObjects) .build(); - verifyRoleInRanger(rangerAuthPlugin, verifyRole); + verifyRoleInRanger(rangerAuthHivePlugin, verifyRole); } } } + @Test + public void testRoleChangeRemoveCatalogSecurableObject() { + String currentFunName = currentFunName(); + Role mockCatalogRole = mockCatalogRole(currentFunName); + + Assertions.assertTrue( + rangerAuthHivePlugin.onRoleUpdated( + mockCatalogRole, + RoleChange.removeSecurableObject( + mockCatalogRole.name(), mockCatalogRole.securableObjects().get(0)))); + assertFindManagedPolicy(mockCatalogRole, false); + } + @Test public void testRoleChangeUpdateSecurableObject() { SecurableObject oldSecurableObject = SecurableObjects.parse( - String.format("catalog.%s.tab1", currentFunName()), - MetadataObject.Type.TABLE, + String.format("catalog1.%s", currentFunName()), + MetadataObject.Type.SCHEMA, Lists.newArrayList(Privileges.CreateTable.allow())); RoleEntity role = RoleEntity.builder() @@ -377,20 +418,18 @@ public void testRoleChangeUpdateSecurableObject() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(oldSecurableObject)) .build(); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); - Role mockCatalogRole = mockCatalogRole(currentFunName()); // Keep the same matedata namespace and type, but change privileges SecurableObject newSecurableObject = SecurableObjects.parse( oldSecurableObject.fullName(), oldSecurableObject.type(), - Lists.newArrayList(Privileges.SelectTable.allow())); + Lists.newArrayList(Privileges.ModifyTable.allow())); Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( - mockCatalogRole, - RoleChange.updateSecurableObject( - mockCatalogRole.name(), oldSecurableObject, newSecurableObject))); + rangerAuthHivePlugin.onRoleUpdated( + role, + RoleChange.updateSecurableObject(role.name(), oldSecurableObject, newSecurableObject))); // construct a verified role to check if the role and Ranger policy are created correctly RoleEntity verifyRole = @@ -400,22 +439,89 @@ public void testRoleChangeUpdateSecurableObject() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(newSecurableObject)) .build(); - verifyRoleInRanger(rangerAuthPlugin, verifyRole); + verifyRoleInRanger(rangerAuthHivePlugin, verifyRole); } @Test + public void testRoleChangeUpdateDifferentMetadata() { + // Test same metadata namespace and different type + SecurableObject metalake = + SecurableObjects.parse( + String.format("%s", currentFunName()), + MetadataObject.Type.METALAKE, + Lists.newArrayList(Privileges.CreateTable.allow())); + SecurableObject catalog = + SecurableObjects.parse( + metalake.fullName(), + MetadataObject.Type.CATALOG, + Lists.newArrayList(Privileges.ModifyTable.allow())); + RoleEntity role = + RoleEntity.builder() + .withId(1L) + .withName(currentFunName()) + .withAuditInfo(auditInfo) + .withSecurableObjects(Lists.newArrayList(metalake)) + .build(); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> + rangerAuthHivePlugin.onRoleUpdated( + role, RoleChange.updateSecurableObject(role.name(), metalake, catalog))); + + // Test different metadata namespace and same type + SecurableObject schema1 = + SecurableObjects.parse( + String.format("catalog1.%s", currentFunName()), + MetadataObject.Type.SCHEMA, + Lists.newArrayList(Privileges.CreateTable.allow())); + SecurableObject schema2 = + SecurableObjects.parse( + String.format("catalog1-diff.%s", currentFunName()), + schema1.type(), + Lists.newArrayList(Privileges.ModifyTable.allow())); + Assertions.assertThrows( + IllegalArgumentException.class, + () -> + rangerAuthHivePlugin.onRoleUpdated( + role, RoleChange.updateSecurableObject(role.name(), schema1, schema2))); + } + + @Test + public void testRoleChangeUpdateCatalogSecurableObject() { + String currentFunName = currentFunName(); + Role mockCatalogRole = mockCatalogRole(currentFunName); + + // Keep the same matedata namespace and type, but change privileges + SecurableObject newSecurableObject = + SecurableObjects.parse( + mockCatalogRole.securableObjects().get(0).fullName(), + mockCatalogRole.securableObjects().get(0).type(), + Lists.newArrayList(Privileges.SelectTable.allow())); + + Assertions.assertTrue( + rangerAuthHivePlugin.onRoleUpdated( + mockCatalogRole, + RoleChange.updateSecurableObject( + mockCatalogRole.name(), + mockCatalogRole.securableObjects().get(0), + newSecurableObject))); + assertFindManagedPolicy(mockCatalogRole, false); + } + public void testRoleChangeCombinedOperation() { MetadataObject oldMetadataObject = MetadataObjects.parse( String.format("catalog.%s.tab1", currentFunName()), MetadataObject.Type.TABLE); String userName = "user1"; - rangerAuthPlugin.onOwnerSet(oldMetadataObject, null, new MockOwner(userName, Owner.Type.USER)); + rangerAuthHivePlugin.onOwnerSet( + oldMetadataObject, null, new MockOwner(userName, Owner.Type.USER)); + verifyOwnerInRanger(oldMetadataObject, Lists.newArrayList(userName)); SecurableObject oldSecurableObject = SecurableObjects.parse( oldMetadataObject.fullName(), MetadataObject.Type.TABLE, - Lists.newArrayList(Privileges.CreateTable.allow())); + Lists.newArrayList(Privileges.ModifyTable.allow())); RoleEntity role = RoleEntity.builder() @@ -424,9 +530,8 @@ public void testRoleChangeCombinedOperation() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(oldSecurableObject)) .build(); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); - Role mockCatalogRole = mockCatalogRole(currentFunName()); // Keep the same matedata namespace and type, but change privileges SecurableObject newSecurableObject = SecurableObjects.parse( @@ -434,10 +539,9 @@ public void testRoleChangeCombinedOperation() { oldSecurableObject.type(), Lists.newArrayList(Privileges.SelectTable.allow())); Assertions.assertTrue( - rangerAuthPlugin.onRoleUpdated( - mockCatalogRole, - RoleChange.updateSecurableObject( - mockCatalogRole.name(), oldSecurableObject, newSecurableObject))); + rangerAuthHivePlugin.onRoleUpdated( + role, + RoleChange.updateSecurableObject(role.name(), oldSecurableObject, newSecurableObject))); // construct a verified role to check if the role and Ranger policy are created correctly RoleEntity verifyRole = @@ -447,16 +551,13 @@ public void testRoleChangeCombinedOperation() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(newSecurableObject)) .build(); - verifyRoleInRanger(rangerAuthPlugin, verifyRole); + verifyRoleInRanger(rangerAuthHivePlugin, verifyRole); verifyOwnerInRanger(oldMetadataObject, Lists.newArrayList(userName)); // Delete the role - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(verifyRole)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(verifyRole)); // Because these metaobjects have an owner, so the policy will not be deleted. - role.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject))); + assertFindManagedPolicy(role, true); verifyOwnerInRanger(oldMetadataObject, Lists.newArrayList(userName)); } @@ -464,7 +565,7 @@ public void testRoleChangeCombinedOperation() { public void testOnGrantedRolesToUser() { // prepare to create a role RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); // granted a role to the user1 String userName1 = "user1"; @@ -477,13 +578,13 @@ public void testOnGrantedRolesToUser() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, Lists.newArrayList(userName1)); // multi-call to granted role to the user1 Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, Lists.newArrayList(userName1)); // granted a role to the user2 String userName2 = "user2"; @@ -496,17 +597,17 @@ public void testOnGrantedRolesToUser() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity2)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity2)); // Same to verify user1 and user2 - verifyRoleInRanger(rangerAuthPlugin, role, Lists.newArrayList(userName1, userName2)); + verifyRoleInRanger(rangerAuthHivePlugin, role, Lists.newArrayList(userName1, userName2)); } @Test public void testOnRevokedRolesFromUser() { // prepare to create a role RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); // granted a role to the user1 String userName1 = "user1"; @@ -519,24 +620,24 @@ public void testOnRevokedRolesFromUser() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, Lists.newArrayList(userName1)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, null, Lists.newArrayList(userName1)); // multi-call to revoked role from user1 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, null, Lists.newArrayList(userName1)); } @Test public void testOnGrantedRolesToGroup() { // prepare to create a role RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); // granted a role to the group1 String groupName1 = "group1"; @@ -549,13 +650,13 @@ public void testOnGrantedRolesToGroup() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, null, Lists.newArrayList(groupName1)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, null, null, Lists.newArrayList(groupName1)); // multi-call to grant a role to the group1 test idempotent operation Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, null, Lists.newArrayList(groupName1)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, null, null, Lists.newArrayList(groupName1)); // granted a role to the group2 String groupName2 = "group2"; @@ -568,18 +669,18 @@ public void testOnGrantedRolesToGroup() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity2)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity2)); // Same to verify group1 and group2 verifyRoleInRanger( - rangerAuthPlugin, role, null, null, Lists.newArrayList(groupName1, groupName2)); + rangerAuthHivePlugin, role, null, null, Lists.newArrayList(groupName1, groupName2)); } @Test public void testOnRevokedRolesFromGroup() { // prepare to create a role RoleEntity role = mock3TableRole(currentFunName()); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role)); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role)); // granted a role to the group1 String groupName1 = "group1"; @@ -592,17 +693,37 @@ public void testOnRevokedRolesFromGroup() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, null, Lists.newArrayList(groupName1)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role), groupEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role, null, null, Lists.newArrayList(groupName1)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role), groupEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, null, null, Lists.newArrayList(groupName1)); + rangerAuthHivePlugin.onRevokedRolesFromGroup(Lists.newArrayList(role), groupEntity1)); + verifyRoleInRanger( + rangerAuthHivePlugin, role, null, null, null, Lists.newArrayList(groupName1)); // multi-call to revoke from the group1 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role), groupEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role, null, null, null, Lists.newArrayList(groupName1)); + rangerAuthHivePlugin.onRevokedRolesFromGroup(Lists.newArrayList(role), groupEntity1)); + verifyRoleInRanger( + rangerAuthHivePlugin, role, null, null, null, Lists.newArrayList(groupName1)); + } + + private void assertFindManagedPolicy(Role role, boolean policyExist) { + role.securableObjects().stream() + .forEach( + securableObject -> + rangerAuthHivePlugin.translatePrivilege(securableObject).stream() + .forEach( + rangerSecurableObject -> { + LOG.info("rangerSecurableObject: " + rangerSecurableObject); + if (policyExist) { + Assertions.assertNotNull( + rangerHelper.findManagedPolicy(rangerSecurableObject)); + } else { + Assertions.assertNull( + rangerHelper.findManagedPolicy(rangerSecurableObject)); + } + })); } private static class MockOwner implements Owner { @@ -627,39 +748,109 @@ public Type type() { @Test public void testOnOwnerSet() { - MetadataObject metadataObject = + MetadataObject schema = MetadataObjects.parse( - String.format("catalog.%s.tab1", currentFunName()), MetadataObject.Type.TABLE); + String.format("catalog.%s", currentFunName()), MetadataObject.Type.SCHEMA); String userName1 = "user1"; Owner owner1 = new MockOwner(userName1, Owner.Type.USER); - Assertions.assertTrue(rangerAuthPlugin.onOwnerSet(metadataObject, null, owner1)); - verifyOwnerInRanger(metadataObject, Lists.newArrayList(userName1), null, null, null); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(schema, null, owner1)); + rangerAuthHivePlugin.translateOwner(schema).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger(rangerSecurableObject, Lists.newArrayList(userName1)); + }); + MetadataObject table = + MetadataObjects.parse( + String.format("catalog.%s.tab1", currentFunName()), MetadataObject.Type.TABLE); String userName2 = "user2"; Owner owner2 = new MockOwner(userName2, Owner.Type.USER); - Assertions.assertTrue(rangerAuthPlugin.onOwnerSet(metadataObject, owner1, owner2)); - verifyOwnerInRanger( - metadataObject, Lists.newArrayList(userName2), Lists.newArrayList(userName1), null, null); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(table, null, owner2)); + rangerAuthHivePlugin.translateOwner(table).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger(rangerSecurableObject, Lists.newArrayList(userName2)); + }); + + String userName3 = "user3"; + Owner owner3 = new MockOwner(userName3, Owner.Type.USER); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(table, owner2, owner3)); + rangerAuthHivePlugin.translateOwner(table).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger( + rangerSecurableObject, + Lists.newArrayList(userName3), + Lists.newArrayList(userName2)); + }); String groupName1 = "group1"; - Owner owner3 = new MockOwner(groupName1, Owner.Type.GROUP); - Assertions.assertTrue(rangerAuthPlugin.onOwnerSet(metadataObject, owner2, owner3)); - verifyOwnerInRanger( - metadataObject, - null, - Lists.newArrayList(userName1, userName2), - Lists.newArrayList(groupName1), - null); + Owner owner4 = new MockOwner(groupName1, Owner.Type.GROUP); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(table, owner3, owner4)); + rangerAuthHivePlugin.translateOwner(table).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger( + rangerSecurableObject, + null, + Lists.newArrayList(userName1, userName2), + Lists.newArrayList(groupName1)); + }); String groupName2 = "group2"; - Owner owner4 = new MockOwner(groupName2, Owner.Type.GROUP); - Assertions.assertTrue(rangerAuthPlugin.onOwnerSet(metadataObject, owner3, owner4)); - verifyOwnerInRanger( - metadataObject, - null, - Lists.newArrayList(userName1, userName2), - Lists.newArrayList(groupName2), - Lists.newArrayList(groupName1)); + Owner owner5 = new MockOwner(groupName2, Owner.Type.GROUP); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(table, owner4, owner5)); + rangerAuthHivePlugin.translateOwner(table).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger( + rangerSecurableObject, + null, + Lists.newArrayList(userName1, userName2), + Lists.newArrayList(groupName2), + Lists.newArrayList(groupName1)); + }); + } + + @Test + public void testOnOwnerSetCatalog() { + MetadataObject metalake = + MetadataObjects.parse( + String.format("metalake-%s", currentFunName()), MetadataObject.Type.METALAKE); + String userName1 = "user1"; + Owner owner1 = new MockOwner(userName1, Owner.Type.USER); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(metalake, null, owner1)); + rangerAuthHivePlugin.translateOwner(metalake).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger( + rangerSecurableObject, + null, + null, + null, + null, + Lists.newArrayList(RangerHelper.GRAVITINO_METALAKE_OWNER_ROLE)); + }); + + MetadataObject catalog = + MetadataObjects.parse( + String.format("catalog-%s", currentFunName()), MetadataObject.Type.CATALOG); + String userName2 = "user2"; + Owner owner2 = new MockOwner(userName2, Owner.Type.USER); + Assertions.assertTrue(rangerAuthHivePlugin.onOwnerSet(catalog, null, owner2)); + rangerAuthHivePlugin.translateOwner(catalog).stream() + .forEach( + rangerSecurableObject -> { + verifyOwnerInRanger( + rangerSecurableObject, + null, + null, + null, + null, + Lists.newArrayList( + RangerHelper.GRAVITINO_METALAKE_OWNER_ROLE, + RangerHelper.GRAVITINO_CATALOG_OWNER_ROLE)); + }); } @Test @@ -672,10 +863,10 @@ public void testCreateUser() { .withRoleIds(null) .withRoleNames(null) .build(); - Assertions.assertTrue(rangerAuthPlugin.onUserAdded(user)); - Assertions.assertTrue(rangerAuthPlugin.onUserAcquired(user)); - Assertions.assertTrue(rangerAuthPlugin.onUserRemoved(user)); - Assertions.assertFalse(rangerAuthPlugin.onUserAcquired(user)); + Assertions.assertTrue(rangerAuthHivePlugin.onUserAdded(user)); + Assertions.assertTrue(rangerAuthHivePlugin.onUserAcquired(user)); + Assertions.assertTrue(rangerAuthHivePlugin.onUserRemoved(user)); + Assertions.assertFalse(rangerAuthHivePlugin.onUserAcquired(user)); } @Test @@ -689,24 +880,25 @@ public void testCreateGroup() { .withRoleNames(null) .build(); - Assertions.assertTrue(rangerAuthPlugin.onGroupAdded(group)); - Assertions.assertTrue(rangerAuthPlugin.onGroupAcquired(group)); - Assertions.assertTrue(rangerAuthPlugin.onGroupRemoved(group)); - Assertions.assertFalse(rangerAuthPlugin.onGroupAcquired(group)); + Assertions.assertTrue(rangerAuthHivePlugin.onGroupAdded(group)); + Assertions.assertTrue(rangerAuthHivePlugin.onGroupAcquired(group)); + Assertions.assertTrue(rangerAuthHivePlugin.onGroupRemoved(group)); + Assertions.assertFalse(rangerAuthHivePlugin.onGroupAcquired(group)); } @Test public void testCombinationOperation() { - // Create a `CreateTable` privilege role + // Create a `ModifyTable` privilege role SecurableObject securableObject1 = SecurableObjects.parse( String.format( "catalog.%s.tab1", currentFunName()), // use unique db name to avoid conflict MetadataObject.Type.TABLE, - Lists.newArrayList(Privileges.CreateTable.allow())); + Lists.newArrayList(Privileges.ModifyTable.allow())); String ownerName = "owner1"; - rangerAuthPlugin.onOwnerSet(securableObject1, null, new MockOwner(ownerName, Owner.Type.USER)); + rangerAuthHivePlugin.onOwnerSet( + securableObject1, null, new MockOwner(ownerName, Owner.Type.USER)); verifyOwnerInRanger(securableObject1, Lists.newArrayList(ownerName), null, null, null); RoleEntity role1 = @@ -716,8 +908,8 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(securableObject1)) .build(); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role1)); - verifyRoleInRanger(rangerAuthPlugin, role1); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role1)); + verifyRoleInRanger(rangerAuthHivePlugin, role1); // Create a `SelectTable` privilege role SecurableObject securableObject2 = @@ -732,8 +924,8 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(securableObject2)) .build(); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role2)); - verifyRoleInRanger(rangerAuthPlugin, role2); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role2)); + verifyRoleInRanger(rangerAuthHivePlugin, role2); // Create a `ModifyTable` privilege role SecurableObject securableObject3 = @@ -748,8 +940,8 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .withSecurableObjects(Lists.newArrayList(securableObject3)) .build(); - Assertions.assertTrue(rangerAuthPlugin.onRoleCreated(role3)); - verifyRoleInRanger(rangerAuthPlugin, role3); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleCreated(role3)); + verifyRoleInRanger(rangerAuthHivePlugin, role3); /** Test grant to user */ // granted role1 to the user1 @@ -763,11 +955,11 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity1)); // multiple call to grant role1 to the user1 to test idempotent operation Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role1, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role1, Lists.newArrayList(userName1)); // granted role1 to the user2 String userName2 = "user2"; @@ -780,8 +972,8 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity2)); - verifyRoleInRanger(rangerAuthPlugin, role1, Lists.newArrayList(userName1, userName2)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity2)); + verifyRoleInRanger(rangerAuthHivePlugin, role1, Lists.newArrayList(userName1, userName2)); // granted role1 to the user3 String userName3 = "user3"; @@ -794,26 +986,26 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity3)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role1), userEntity3)); verifyRoleInRanger( - rangerAuthPlugin, role1, Lists.newArrayList(userName1, userName2, userName3)); + rangerAuthHivePlugin, role1, Lists.newArrayList(userName1, userName2, userName3)); // Same granted role2 and role3 to the user1 and user2 and user3 Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity1)); - verifyRoleInRanger(rangerAuthPlugin, role2, Lists.newArrayList(userName1)); - verifyRoleInRanger(rangerAuthPlugin, role3, Lists.newArrayList(userName1)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity1)); + verifyRoleInRanger(rangerAuthHivePlugin, role2, Lists.newArrayList(userName1)); + verifyRoleInRanger(rangerAuthHivePlugin, role3, Lists.newArrayList(userName1)); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity2)); - verifyRoleInRanger(rangerAuthPlugin, role2, Lists.newArrayList(userName1, userName2)); - verifyRoleInRanger(rangerAuthPlugin, role3, Lists.newArrayList(userName1, userName2)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity2)); + verifyRoleInRanger(rangerAuthHivePlugin, role2, Lists.newArrayList(userName1, userName2)); + verifyRoleInRanger(rangerAuthHivePlugin, role3, Lists.newArrayList(userName1, userName2)); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity3)); + rangerAuthHivePlugin.onGrantedRolesToUser(Lists.newArrayList(role2, role3), userEntity3)); verifyRoleInRanger( - rangerAuthPlugin, role2, Lists.newArrayList(userName1, userName2, userName3)); + rangerAuthHivePlugin, role2, Lists.newArrayList(userName1, userName2, userName3)); verifyRoleInRanger( - rangerAuthPlugin, role3, Lists.newArrayList(userName1, userName2, userName3)); + rangerAuthHivePlugin, role3, Lists.newArrayList(userName1, userName2, userName3)); /** Test grant to group */ // granted role1 to the group1 @@ -827,9 +1019,9 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity1)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, Lists.newArrayList(userName1, userName2, userName3), null, @@ -846,9 +1038,9 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity2)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, Lists.newArrayList(userName1, userName2, userName3), null, @@ -865,9 +1057,9 @@ public void testCombinationOperation() { .withAuditInfo(auditInfo) .build(); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity3)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role1), groupEntity3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, Lists.newArrayList(userName1, userName2, userName3), null, @@ -875,44 +1067,44 @@ public void testCombinationOperation() { // Same granted role2 and role3 to the group1 and group2 and group3 Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity1)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1)); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity2)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1, groupName2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1, groupName2)); Assertions.assertTrue( - rangerAuthPlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity3)); + rangerAuthHivePlugin.onGrantedRolesToGroup(Lists.newArrayList(role2, role3), groupEntity3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1, groupName2, groupName3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, Lists.newArrayList(userName1, userName2, userName3), null, @@ -921,9 +1113,9 @@ public void testCombinationOperation() { /** Test revoke from user */ // revoke role1 from the user1 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity1)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, Lists.newArrayList(userName2, userName3), Lists.newArrayList(userName1), @@ -931,9 +1123,9 @@ public void testCombinationOperation() { // revoke role1 from the user2 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity2)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, Lists.newArrayList(userName3), Lists.newArrayList(userName1, userName2), @@ -941,9 +1133,9 @@ public void testCombinationOperation() { // revoke role1 from the user3 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity3)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role1), userEntity3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, null, Lists.newArrayList(userName1, userName2, userName3), @@ -951,46 +1143,46 @@ public void testCombinationOperation() { // Same revoke role2 and role3 from the user1 and user2 and user3 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity1)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, Lists.newArrayList(userName2, userName3), Lists.newArrayList(userName1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, Lists.newArrayList(userName2, userName3), Lists.newArrayList(userName1)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity2)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, Lists.newArrayList(userName3), Lists.newArrayList(userName1, userName2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, Lists.newArrayList(userName3), Lists.newArrayList(userName1, userName2)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity3)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity3)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity3)); + rangerAuthHivePlugin.onRevokedRolesFromUser(Lists.newArrayList(role2, role3), userEntity3)); verifyRoleInRanger( - rangerAuthPlugin, role2, null, Lists.newArrayList(userName1, userName2, userName3)); + rangerAuthHivePlugin, role2, null, Lists.newArrayList(userName1, userName2, userName3)); verifyRoleInRanger( - rangerAuthPlugin, role3, null, Lists.newArrayList(userName1, userName2, userName3)); + rangerAuthHivePlugin, role3, null, Lists.newArrayList(userName1, userName2, userName3)); /** Test revoke from group */ // revoke role1 from the group1 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity1)); + rangerAuthHivePlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, null, Lists.newArrayList(userName1, userName2, userName3), @@ -999,9 +1191,9 @@ public void testCombinationOperation() { // revoke role1 from the group2 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity2)); + rangerAuthHivePlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, null, Lists.newArrayList(userName1, userName2, userName3), @@ -1010,9 +1202,9 @@ public void testCombinationOperation() { // revoke role1 from the group3 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity3)); + rangerAuthHivePlugin.onRevokedRolesFromGroup(Lists.newArrayList(role1), groupEntity3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role1, null, Lists.newArrayList(userName1, userName2, userName3), @@ -1021,16 +1213,17 @@ public void testCombinationOperation() { // Same revoke role2 and role3 from the group1 and group2 and group3 Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role2, role3), groupEntity1)); + rangerAuthHivePlugin.onRevokedRolesFromGroup( + Lists.newArrayList(role2, role3), groupEntity1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, null, Lists.newArrayList(userName1, userName2, userName3), Lists.newArrayList(groupName2, groupName3), Lists.newArrayList(groupName1)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, null, Lists.newArrayList(userName1, userName2, userName3), @@ -1038,16 +1231,17 @@ public void testCombinationOperation() { Lists.newArrayList(groupName1)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role2, role3), groupEntity2)); + rangerAuthHivePlugin.onRevokedRolesFromGroup( + Lists.newArrayList(role2, role3), groupEntity2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, null, Lists.newArrayList(userName1, userName2, userName3), Lists.newArrayList(groupName3), Lists.newArrayList(groupName1, groupName2)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, null, Lists.newArrayList(userName1, userName2, userName3), @@ -1055,49 +1249,59 @@ public void testCombinationOperation() { Lists.newArrayList(groupName1, groupName2)); Assertions.assertTrue( - rangerAuthPlugin.onRevokedRolesFromGroup(Lists.newArrayList(role2, role3), groupEntity3)); + rangerAuthHivePlugin.onRevokedRolesFromGroup( + Lists.newArrayList(role2, role3), groupEntity3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role2, null, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1, groupName2, groupName3)); verifyRoleInRanger( - rangerAuthPlugin, + rangerAuthHivePlugin, role3, null, Lists.newArrayList(userName1, userName2, userName3), null, Lists.newArrayList(groupName1, groupName2, groupName3)); - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(role1)); - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(role2)); - Assertions.assertTrue(rangerAuthPlugin.onRoleDeleted(role3)); - // Because these metaobjects have owner, so the policy will not be deleted. - role1.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject))); - role2.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject))); - role3.securableObjects().stream() - .forEach( - securableObject -> - Assertions.assertNotNull(rangerHelper.findManagedPolicy(securableObject))); + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(role1)); + // Because role1's secrable object have owner, so the policy will not be deleted. + assertFindManagedPolicy(role1, true); + + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(role2)); + assertFindManagedPolicy(role2, true); + + Assertions.assertTrue(rangerAuthHivePlugin.onRoleDeleted(role3)); + assertFindManagedPolicy(role3, true); } - /** Verify the Gravitino role in Ranger service */ + private static String generatePolicyName(MetadataObject metadataObject) { + List nsMetadataObject = + Lists.newArrayList(SecurableObjects.DOT_SPLITTER.splitToList(metadataObject.fullName())); + if (!(metadataObject instanceof RangerSecurableObject) + && metadataObject.type() != MetadataObject.Type.METALAKE) { + nsMetadataObject.remove(0); // remove `catalog` + } + return String.join(".", nsMetadataObject); + } + + /** + * Verify the Gravitino role in Ranger service + * + *

metadataObject: the Gravitino securable object to be verified + */ private void verifyOwnerInRanger( MetadataObject metadataObject, List includeUsers, List excludeUsers, List includeGroups, - List excludeGroups) { + List excludeGroups, + List includeRoles, + List excludeRoles) { // Find policy by each metadata Object - String policyName = metadataObject.fullName(); + String policyName = generatePolicyName(metadataObject); RangerPolicy policy; try { policy = RangerITEnv.rangerClient.getPolicy(RangerITEnv.RANGER_HIVE_REPO_NAME, policyName); @@ -1113,7 +1317,11 @@ private void verifyOwnerInRanger( // verify namespace List metaObjNamespaces = Lists.newArrayList(DOT_SPLITTER.splitToList(metadataObject.fullName())); - metaObjNamespaces.remove(0); // skip catalog + if (!(metadataObject instanceof RangerSecurableObject) + && metadataObject.type() != MetadataObject.Type.METALAKE) { + metaObjNamespaces.remove(0); // skip catalog + } + List rolePolicies = new ArrayList<>(); for (int i = 0; i < metaObjNamespaces.size(); i++) { rolePolicies.add( @@ -1135,7 +1343,7 @@ private void verifyOwnerInRanger( return policyItem.getAccesses().stream() .anyMatch( access -> { - return rangerAuthPlugin + return rangerAuthHivePlugin .ownerMappingRule() .contains(RangerPrivileges.valueOf(access.getType())); }); @@ -1174,15 +1382,72 @@ private void verifyOwnerInRanger( return false; } } + if (includeRoles != null && !includeRoles.isEmpty()) { + if (!policyItem.getRoles().containsAll(includeRoles)) { + return false; + } + } + if (excludeRoles != null && !excludeRoles.isEmpty()) { + boolean containExcludeRole = + policyItem.getRoles().stream() + .anyMatch( + role -> { + return excludeRoles.contains(role); + }); + if (containExcludeRole) { + return false; + } + } return true; }); } private void verifyOwnerInRanger(MetadataObject metadataObject) { - verifyOwnerInRanger(metadataObject, null, null, null, null); + verifyOwnerInRanger(metadataObject, null, null, null, null, null, null); } private void verifyOwnerInRanger(MetadataObject metadataObject, List includeUsers) { - verifyOwnerInRanger(metadataObject, includeUsers, null, null, null); + verifyOwnerInRanger(metadataObject, includeUsers, null, null, null, null, null); + } + + private void verifyOwnerInRanger( + MetadataObject metadataObject, List includeUsers, List excludeUsers) { + verifyOwnerInRanger(metadataObject, includeUsers, excludeUsers, null, null, null, null); + } + + private void verifyOwnerInRanger( + MetadataObject metadataObject, + List includeUsers, + List excludeUsers, + List includeGroups) { + verifyOwnerInRanger( + metadataObject, includeUsers, excludeUsers, includeGroups, null, null, null); + } + + private void verifyOwnerInRanger( + MetadataObject metadataObject, + List includeUsers, + List excludeUsers, + List includeGroups, + List excludeGroups) { + verifyOwnerInRanger( + metadataObject, includeUsers, excludeUsers, includeGroups, excludeGroups, null, null); + } + + private void verifyOwnerInRanger( + MetadataObject metadataObject, + List includeUsers, + List excludeUsers, + List includeGroups, + List excludeGroups, + List includeRoles) { + verifyOwnerInRanger( + metadataObject, + includeUsers, + excludeUsers, + includeGroups, + excludeGroups, + includeRoles, + null); } } diff --git a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerITEnv.java b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerITEnv.java index 563ec7fd1f7..1a5218b08e7 100644 --- a/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerITEnv.java +++ b/authorizations/authorization-ranger/src/test/java/org/apache/gravitino/authorization/ranger/integration/test/RangerITEnv.java @@ -25,13 +25,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import org.apache.gravitino.authorization.Role; +import org.apache.gravitino.authorization.ranger.RangerAuthorizationHivePlugin; import org.apache.gravitino.authorization.ranger.RangerAuthorizationPlugin; import org.apache.gravitino.authorization.ranger.RangerHelper; -import org.apache.gravitino.authorization.ranger.RangerPrivilege; +import org.apache.gravitino.authorization.ranger.RangerPrivileges; +import org.apache.gravitino.authorization.ranger.RangerSecurableObject; import org.apache.gravitino.authorization.ranger.reference.RangerDefines; +import org.apache.gravitino.connector.AuthorizationPropertiesMeta; import org.apache.gravitino.integration.test.container.ContainerSuite; import org.apache.gravitino.integration.test.container.HiveContainer; import org.apache.gravitino.integration.test.container.RangerContainer; @@ -76,11 +78,37 @@ public class RangerITEnv { public static final String SEARCH_FILTER_COLUMN = SearchFilter.RESOURCE_PREFIX + RESOURCE_COLUMN; // Search filter prefix file path constants public static final String SEARCH_FILTER_PATH = SearchFilter.RESOURCE_PREFIX + RESOURCE_PATH; + public static RangerAuthorizationPlugin rangerAuthHivePlugin; + protected static RangerHelper rangerHelper; - public static void setup() { + public static void init() { containerSuite.startRangerContainer(); rangerClient = containerSuite.getRangerContainer().rangerClient; + rangerAuthHivePlugin = + RangerAuthorizationHivePlugin.getInstance( + ImmutableMap.of( + AuthorizationPropertiesMeta.RANGER_ADMIN_URL, + String.format( + "http://%s:%d", + containerSuite.getRangerContainer().getContainerIpAddress(), + RangerContainer.RANGER_SERVER_PORT), + AuthorizationPropertiesMeta.RANGER_AUTH_TYPE, + RangerContainer.authType, + AuthorizationPropertiesMeta.RANGER_USERNAME, + RangerContainer.rangerUserName, + AuthorizationPropertiesMeta.RANGER_PASSWORD, + RangerContainer.rangerPassword, + AuthorizationPropertiesMeta.RANGER_SERVICE_NAME, + RangerITEnv.RANGER_HIVE_REPO_NAME)); + rangerHelper = + new RangerHelper( + rangerClient, + RangerContainer.rangerUserName, + RangerITEnv.RANGER_HIVE_REPO_NAME, + rangerAuthHivePlugin.ownerMappingRule(), + rangerAuthHivePlugin.policyResourceDefinesRule()); + if (!initRangerService) { synchronized (RangerITEnv.class) { // No IP address set, no impact on testing @@ -146,11 +174,11 @@ static void allowAnyoneAccessHDFS() { policyItem.setAccesses( Arrays.asList( new RangerPolicy.RangerPolicyItemAccess( - RangerPrivilege.RangerHdfsPrivilege.READ.toString()), + RangerPrivileges.RangerHdfsPrivilege.READ.toString()), new RangerPolicy.RangerPolicyItemAccess( - RangerPrivilege.RangerHdfsPrivilege.WRITE.toString()), + RangerPrivileges.RangerHdfsPrivilege.WRITE.toString()), new RangerPolicy.RangerPolicyItemAccess( - RangerPrivilege.RangerHdfsPrivilege.EXECUTE.toString()))); + RangerPrivileges.RangerHdfsPrivilege.EXECUTE.toString()))); updateOrCreateRangerPolicy( RANGER_HDFS_TYPE, RANGER_HDFS_REPO_NAME, @@ -187,7 +215,7 @@ static void allowAnyoneAccessInformationSchema() { policyItem.setAccesses( Arrays.asList( new RangerPolicy.RangerPolicyItemAccess( - RangerPrivilege.RangerHivePrivilege.SELECT.toString()))); + RangerPrivileges.RangerHivePrivilege.SELECT.toString()))); updateOrCreateRangerPolicy( RANGER_HIVE_TYPE, RANGER_HIVE_REPO_NAME, @@ -400,42 +428,42 @@ protected static void verifyRoleInRanger( role.securableObjects() .forEach( securableObject -> { - RangerPolicy policy; - try { - policy = - RangerITEnv.rangerClient.getPolicy( - RangerITEnv.RANGER_HIVE_REPO_NAME, securableObject.fullName()); - LOG.info("policy: " + policy.toString()); - } catch (RangerServiceException e) { - LOG.error("Failed to get policy: " + securableObject.fullName()); - throw new RuntimeException(e); - } - - securableObject - .privileges() - .forEach( - gravitinoPrivilege -> { - Set mappedPrivileges = - rangerAuthPlugin.translatePrivilege(gravitinoPrivilege.name()); - - boolean match = - policy.getPolicyItems().stream() - .filter( - policyItem -> { - // Filter Ranger policy item by Gravitino privilege - return policyItem.getAccesses().stream() - .anyMatch( - access -> { - return mappedPrivileges.contains(access.getType()); - }); - }) - .allMatch( - policyItem -> { - // Verify role name in Ranger policy item - return policyItem.getRoles().contains(role.name()); - }); - Assertions.assertTrue(match); - }); + List rangerSecurableObjects = + rangerAuthPlugin.translatePrivilege(securableObject); + + rangerSecurableObjects.forEach( + rangerSecurableObject -> { + RangerPolicy policy; + try { + policy = + RangerITEnv.rangerClient.getPolicy( + RangerITEnv.RANGER_HIVE_REPO_NAME, rangerSecurableObject.fullName()); + LOG.info("policy: " + policy.toString()); + } catch (RangerServiceException e) { + LOG.error("Failed to get policy: " + securableObject.fullName()); + throw new RuntimeException(e); + } + boolean match = + policy.getPolicyItems().stream() + .filter( + policyItem -> { + // Filter Ranger policy item by Gravitino privilege + return policyItem.getAccesses().stream() + .anyMatch( + access -> { + return rangerSecurableObject + .privileges() + .contains( + RangerPrivileges.valueOf(access.getType())); + }); + }) + .allMatch( + policyItem -> { + // Verify role name in Ranger policy item + return policyItem.getRoles().contains(role.name()); + }); + Assertions.assertTrue(match); + }); }); } diff --git a/build.gradle.kts b/build.gradle.kts index 5421348a91c..20ebfa1fb6e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -512,6 +512,7 @@ tasks.rat { "DISCLAIMER.txt", "ROADMAP.md", "clients/client-python/.pytest_cache/*", + "clients/client-python/**/__pycache__", "clients/client-python/.venv/*", "clients/client-python/venv/*", "clients/client-python/apache_gravitino.egg-info/*",