Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#5116][#5106][#4616][#5135] improve(auth-ranger): The owner of catalog/metalake should have all the privileges of schemas/tables #5113

Merged
merged 6 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/access-control-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.**"
yuqi1129 marked this conversation as resolved.
Show resolved Hide resolved
./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
Expand Down
8 changes: 7 additions & 1 deletion api/src/main/java/org/apache/gravitino/MetadataObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,13 @@ public static MetadataObject parse(String fullName, MetadataObject.Type type) {
return MetadataObjects.of(parts, type);
}

private static String getParentFullName(List<String> 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<String> names) {
if (names.size() <= 1) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> config) {
Expand All @@ -46,11 +60,18 @@ public static synchronized RangerAuthorizationHivePlugin getInstance(Map<String,
return instance;
}

@Override
/** Set the default mapping Gravitino privilege name to the Ranger rule */
public Map<Privilege.Name, Set<RangerPrivilege>> 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,
Expand All @@ -60,16 +81,286 @@ public Map<Privilege.Name, Set<RangerPrivilege>> privilegesMappingRule() {
ImmutableSet.of(RangerHivePrivilege.READ, RangerHivePrivilege.SELECT));
}

@Override
/** Set the default owner rule. */
public Set<RangerPrivilege> ownerMappingRule() {
return ImmutableSet.of(RangerHivePrivilege.ALL);
}

@Override
/** Set Ranger policy resource rule. */
public List<String> policyResourceDefinesRule() {
return ImmutableList.of(
PolicyResource.DATABASE.getName(),
PolicyResource.TABLE.getName(),
PolicyResource.COLUMN.getName());
}

@Override
/** Allow privilege operation defines rule. */
public Set<Privilege.Name> 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<RangerSecurableObject> translateOwner(MetadataObject metadataObject) {
List<RangerSecurableObject> 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<RangerSecurableObject> translatePrivilege(SecurableObject securableObject) {
List<RangerSecurableObject> rangerSecurableObjects = new ArrayList<>();

securableObject.privileges().stream()
.filter(Objects::nonNull)
.forEach(
gravitinoPrivilege -> {
Set<RangerPrivilege> 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<String> convertToRangerMetadataObject(MetadataObject metadataObject) {
Preconditions.checkArgument(
!(metadataObject instanceof RangerPrivileges),
"The metadata object must be not a RangerPrivileges object.");
List<String> nsMetadataObject =
Lists.newArrayList(SecurableObjects.DOT_SPLITTER.splitToList(metadataObject.fullName()));
nsMetadataObject.remove(0); // remove the catalog name
return nsMetadataObject;
}
}
Loading
Loading