jerqi commented on code in PR #4515:
URL: https://github.com/apache/gravitino/pull/4515#discussion_r1724906342


##########
authorizations/authorization-ranger/src/main/java/org/apache/gravitino/authorization/ranger/RangerHelper.java:
##########
@@ -0,0 +1,533 @@
+/*
+ * 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.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+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.Arrays;
+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.authorization.ranger.reference.RangerDefines;
+import org.apache.gravitino.exceptions.AuthorizationPluginException;
+import org.apache.ranger.RangerServiceException;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerRole;
+import org.apache.ranger.plugin.util.GrantRevokeRoleRequest;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is a helper class for the Ranger authorization plugin. It 
provides the ability to
+ * manage the Ranger policies and roles.
+ */
+public class RangerHelper {
+  private static final Logger LOG = 
LoggerFactory.getLogger(RangerHelper.class);
+  public static final String MANAGED_BY_GRAVITINO = "MANAGED_BY_GRAVITINO";
+
+  RangerAuthorizationPlugin rangerAuthorizationPlugin;
+
+  /** Mapping Gravitino privilege name to the underlying authorization system 
privileges. */
+  protected Map<Privilege.Name, Set<String>> privilegesMapping = null;
+  /** The owner privileges, the owner can do anything on the metadata object */
+  protected Set<String> ownerPrivileges = null;
+
+  /**
+   * Because Ranger doesn't support the precise search, Ranger will return the 
policy meets the
+   * wildcard(*,?) conditions, If you use `db.table` condition to search 
policy, the Ranger will
+   * match `db1.table1`, `db1.table2`, `db*.table*`, So we need to manually 
precisely filter this
+   * research results. <br>
+   * policySearchKeys: The search Ranger policy condition key defines. <br>
+   * policyPreciseFilterKeys: The precise filter Ranger search results key 
defines <br>
+   */
+  protected List<String> policySearchKeys = null;
+
+  protected List<String> policyPreciseFilterKeys = null;
+
+  public RangerHelper(RangerAuthorizationPlugin rangerAuthorizationPlugin, 
String catalogProvider) {
+    this.rangerAuthorizationPlugin = rangerAuthorizationPlugin;
+    switch (catalogProvider) {
+      case "hive":
+        initPrivilegesMapping();
+        initOwnerPrivileges();
+        initPolicySearchKeys();
+        initPreciseFilterKeys();
+        break;
+      default:
+        throw new IllegalArgumentException(
+            "Authorization plugin unsupported catalog provider: " + 
catalogProvider);
+    }
+  }
+
+  /** Initial mapping Gravitino privilege name to the underlying authorization 
system privileges. */
+  private void initPrivilegesMapping() {
+    privilegesMapping =
+        ImmutableMap.<Privilege.Name, Set<String>>builder()
+            .put(
+                Privilege.Name.CREATE_SCHEMA,
+                ImmutableSet.of(RangerDefines.ACCESS_TYPE_HIVE_CREATE))
+            .put(
+                Privilege.Name.CREATE_TABLE, 
ImmutableSet.of(RangerDefines.ACCESS_TYPE_HIVE_CREATE))
+            .put(
+                Privilege.Name.MODIFY_TABLE,
+                ImmutableSet.of(
+                    RangerDefines.ACCESS_TYPE_HIVE_UPDATE,
+                    RangerDefines.ACCESS_TYPE_HIVE_ALTER,
+                    RangerDefines.ACCESS_TYPE_HIVE_WRITE))
+            .put(
+                Privilege.Name.SELECT_TABLE,
+                ImmutableSet.of(
+                    RangerDefines.ACCESS_TYPE_HIVE_READ, 
RangerDefines.ACCESS_TYPE_HIVE_SELECT))
+            .build();
+  }
+
+  /** Initial Owner privileges */
+  private void initOwnerPrivileges() {
+    ownerPrivileges = ImmutableSet.of(RangerDefines.ACCESS_TYPE_HIVE_ALL);
+  }
+
+  /** Initial Ranger policy search key defines */
+  private void initPolicySearchKeys() {
+    policySearchKeys =
+        Arrays.asList(
+            RangerDefines.SEARCH_FILTER_DATABASE,
+            RangerDefines.SEARCH_FILTER_TABLE,
+            RangerDefines.SEARCH_FILTER_COLUMN);
+  }
+
+  /** Initial precise filter key defines */
+  private void initPreciseFilterKeys() {
+    policyPreciseFilterKeys =
+        Arrays.asList(
+            RangerDefines.RESOURCE_DATABASE,
+            RangerDefines.RESOURCE_TABLE,
+            RangerDefines.RESOURCE_COLUMN);
+  }
+
+  /**
+   * For easy management, each privilege will create one 
RangerPolicyItemAccess in the policy.
+   *
+   * @param policyItem The policy item to check
+   * @throws AuthorizationPluginException If the policy item contains more 
than one access type
+   */
+  void checkPolicyItemAccess(RangerPolicy.RangerPolicyItem policyItem)
+      throws AuthorizationPluginException {
+    if (policyItem.getAccesses().size() != 1) {
+      throw new AuthorizationPluginException(
+          "The access type only have one in the delegate Gravitino management 
policy");
+    }
+    Set<String> setAccesses = new HashSet<>();
+    policyItem
+        .getAccesses()
+        .forEach(
+            access -> {
+              if (setAccesses.contains(access.getType())) {
+                throw new AuthorizationPluginException(
+                    "Contain duplicate privilege(%s) in the delegate Gravitino 
management policy ",
+                    access.getType());
+              }
+              setAccesses.add(access.getType());
+            });
+  }
+
+  /**
+   * Add policy item access items base the securable object's privileges. <br>
+   * We cannot clean the policy items because one Ranger policy maybe contains 
multiple Gravitino
+   * securable objects. <br>
+   */
+  void addPolicyItem(RangerPolicy policy, String roleName, SecurableObject 
securableObject) {
+    // First check the privilege if support in the Ranger Hive
+    checkPrivileges(securableObject);
+
+    // Add the policy items by the securable object's privileges
+    securableObject
+        .privileges()
+        .forEach(
+            gravitinoPrivilege -> {
+              // Translate the Gravitino privilege to map Ranger privilege
+              rangerAuthorizationPlugin
+                  .translatePrivilege(gravitinoPrivilege.name())
+                  .forEach(
+                      mappedPrivilege -> {
+                        // Find the policy item that matches Gravitino 
privilege
+                        List<RangerPolicy.RangerPolicyItem> matchPolicyItems =
+                            policy.getPolicyItems().stream()
+                                .filter(
+                                    policyItem -> {
+                                      return policyItem.getAccesses().stream()
+                                          .anyMatch(
+                                              access -> 
access.getType().equals(mappedPrivilege));
+                                    })
+                                .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(mappedPrivilege);
+                          policyItem.getAccesses().add(access);
+                          policyItem.getRoles().add(roleName);
+                          if (Privilege.Condition.ALLOW == 
gravitinoPrivilege.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);
+                                    }
+                                  });
+                        }
+                      });
+            });
+  }
+
+  /**
+   * Remove policy item base the securable object's privileges and role name. 
<br>
+   * We cannot directly clean the policy items because one Ranger policy maybe 
contains multiple
+   * Gravitino privilege objects. <br>
+   */
+  void removePolicyItem(RangerPolicy policy, String roleName, SecurableObject 
securableObject) {
+    // First check the privilege if support in the Ranger Hive
+    checkPrivileges(securableObject);
+
+    // Delete the policy role base the securable object's privileges
+    policy.getPolicyItems().stream()
+        .forEach(
+            policyItem -> {
+              policyItem
+                  .getAccesses()
+                  .forEach(
+                      access -> {
+                        boolean matchPrivilege =
+                            securableObject.privileges().stream()
+                                .filter(Objects::nonNull)
+                                .flatMap(
+                                    privilege ->
+                                        rangerAuthorizationPlugin
+                                            
.translatePrivilege(privilege.name()).stream())
+                                .filter(Objects::nonNull)
+                                .anyMatch(
+                                    privilege -> {
+                                      return 
access.getType().equals(privilege);
+                                    });
+                        if (matchPrivilege
+                            && !policyItem.getUsers().isEmpty()
+                            && !policyItem.getGroups().isEmpty()) {
+                          // Not ownership policy item, then remove the role
+                          policyItem.getRoles().removeIf(roleName::equals);
+                        }
+                      });
+            });
+
+    // Delete the policy items if the roles are empty and not ownership policy 
item
+    policy
+        .getPolicyItems()
+        .removeIf(
+            policyItem -> {
+              return policyItem.getRoles().isEmpty()
+                  && policyItem.getUsers().isEmpty()
+                  && policyItem.getGroups().isEmpty();
+            });
+  }
+
+  /**
+   * 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.
+   *
+   * @param metadataObject The metadata object to find the managed policy.
+   * @return The managed policy for the metadata object.
+   */
+  @VisibleForTesting
+  public RangerPolicy findManagedPolicy(MetadataObject metadataObject)
+      throws AuthorizationPluginException {
+    List<String> nsMetadataObj =
+        
Lists.newArrayList(SecurableObjects.DOT_SPLITTER.splitToList(metadataObject.fullName()));
+    nsMetadataObj.remove(0); // skip `catalog`
+    Map<String, String> policyFilter = new HashMap<>();
+    Map<String, String> preciseFilterKeysFilter = new HashMap<>();
+    policyFilter.put(
+        RangerDefines.SEARCH_FILTER_SERVICE_NAME, 
rangerAuthorizationPlugin.rangerServiceName);
+    policyFilter.put(SearchFilter.POLICY_LABELS_PARTIAL, MANAGED_BY_GRAVITINO);
+    for (int i = 0; i < nsMetadataObj.size(); i++) {
+      policyFilter.put(policySearchKeys.get(i), nsMetadataObj.get(i));
+      preciseFilterKeysFilter.put(policyPreciseFilterKeys.get(i), 
nsMetadataObj.get(i));
+    }
+
+    try {
+      List<RangerPolicy> policies =
+          rangerAuthorizationPlugin.rangerClient.findPolicies(policyFilter);
+
+      if (!policies.isEmpty()) {
+        /**
+         * Because Ranger doesn't support the precise search, Ranger will 
return the policy meets
+         * the wildcard(*,?) conditions, If you use `db.table` condition to 
search policy, the
+         * Ranger will match `db1.table1`, `db1.table2`, `db*.table*`, So we 
need to manually
+         * precisely filter this research results.
+         */
+        policies =
+            policies.stream()
+                .filter(
+                    policy ->
+                        policy.getResources().entrySet().stream()
+                            .allMatch(
+                                entry ->
+                                    
preciseFilterKeysFilter.containsKey(entry.getKey())
+                                        && entry.getValue().getValues().size() 
== 1
+                                        && entry
+                                            .getValue()
+                                            .getValues()
+                                            
.contains(preciseFilterKeysFilter.get(entry.getKey()))))
+                .collect(Collectors.toList());
+      }
+
+      // Only return the policies that are managed by Gravitino.
+      if (policies.size() > 1) {
+        throw new AuthorizationPluginException(
+            "Each metadata object only have one Gravitino management enable 
policies.");
+      }
+
+      RangerPolicy policy = policies.size() == 1 ? policies.get(0) : null;

Review Comment:
   I and @yuqi prefer
   ```
   if (policies.isEmpty()) {
      return  null;
   }
   policy = policies.get(0);
   policy.getPolicyItems().forEach(this::checkPolicyItemAccess);
   policy.getDenyPolicyItems().forEach(this::checkPolicyItemAccess);
   policy.getRowFilterPolicyItems().forEach(this::checkPolicyItemAccess);
   policy.getDataMaskPolicyItems().forEach(this::checkPolicyItemAccess);
   ````
   It's more readable.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to