[ 
https://issues.apache.org/jira/browse/DRILL-8548?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18103934#comment-18103934
 ] 

ASF GitHub Bot commented on DRILL-8548:
---------------------------------------

shfshihuafeng commented on code in PR #3056:
URL: https://github.com/apache/drill/pull/3056#discussion_r3763934559


##########
drill-ranger/drill-ranger-plugin/src/main/java/org/apache/ranger/authorization/drill/authorizer/DrillAccessControl.java:
##########
@@ -0,0 +1,287 @@
+/*
+ * 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.ranger.authorization.drill.authorizer;
+
+
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.authorization.drill.resource.DrillAccessType;
+import org.apache.ranger.authorization.drill.resource.DrillResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Drill-facing authorization facade and single entry point for all Ranger
+ * access checks from Drill core hook points.
+ *
+ * <p>This class is designed to minimize the code changes required in Drill 
core.
+ * Each hook point is a single static method call:</p>
+ *
+ * <pre>{@code
+ * if (!DrillAccessControl.checkTableAccess(userName, storageEngineName, 
schemaPath, tableName)) {
+ *   throw UserException.permissionError()
+ *       .message("Access denied for user %s on table %s.%s", userName, 
schemaPath, tableName)
+ *       .build();
+ * }
+ * }</pre>
+ *
+ * <p>The class is initialized once at Drillbit startup via {@link 
#init(String)}.
+ * Until initialized, {@link #isEnabled()} returns {@code false} and all checks
+ * pass (fail-open), so the plugin is non-intrusive when disabled.</p>
+ *
+ * <p>The service name is read from the configuration property
+ * {@code ranger.plugin.drill.service.name} (defined in 
ranger-drill-security.xml),
+ * but can also be passed directly to {@link #init(String)} for 
flexibility.</p>
+ */
+public class DrillAccessControl {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(DrillAccessControl.class);
+
+  private static volatile boolean enabled = false;
+  private static volatile DrillAuthorizer authorizer;
+
+  // Set of system schemas that bypass authorization (information_schema, sys, 
etc.)
+  // Stored in uppercase; isSystemSchema() uppercases input before lookup so 
the
+  // bypass is case-insensitive (e.g. "information_schema", 
"INFORMATION_SCHEMA",
+  // "Sys", "SYS" all match).
+  private static final Set<String> SYSTEM_SCHEMAS = new 
HashSet<>(Arrays.asList(
+      "INFORMATION_SCHEMA", "SYS"
+  ));
+
+  private DrillAccessControl() {
+  }
+
+  /**
+   * Initializes the Ranger Drill plugin. Called once at Drillbit startup.
+   *
+   * @param serviceName the Ranger service instance name (must match a service 
created in Ranger Admin)
+   */
+  public static synchronized void init(String serviceName) {
+    if (authorizer != null) {
+      return;
+    }
+    try {
+      LOG.info("Initializing Ranger Drill authorization plugin for service: 
{}", serviceName);
+      authorizer = new DrillAuthorizer(serviceName);
+      enabled = true;
+      LOG.info("Ranger Drill authorization plugin initialized successfully");
+    } catch (Exception e) {
+      LOG.error("Failed to initialize Ranger Drill plugin — authorization 
DISABLED", e);
+      throw new RuntimeException("Failed to initialize Ranger Drill plugin — 
authorization disabled "+ serviceName + " with exception: " + e);
+    }
+  }
+
+  /**
+   * @return {@code true} if the Ranger plugin is initialized
+   */
+  public static boolean isEnabled() {
+    return enabled;
+  }
+
+  /**
+   * Resolves the OS-level groups for a given user via Hadoop UGI.
+   *
+   * @param user the username
+   * @return a set of group names (never null, empty on failure)
+   */
+  public static Set<String> getUserGroups(String user) {
+    if (user == null || user.trim().isEmpty()) {
+      return Collections.emptySet();
+    }
+    try {
+      UserGroupInformation ugi = UserGroupInformation.createRemoteUser(user);
+      String[] groups = ugi.getGroupNames();
+      return groups == null ? Collections.emptySet() : new 
HashSet<>(Arrays.asList(groups));
+    } catch (Exception e) {
+      LOG.warn("Failed to determine groups for user={}", user, e);
+      return Collections.emptySet();
+    }
+  }
+
+  /**
+   * Checks table-level access. This is the external entry point that accepts
+   * the operator as a string (e.g. "SELECT", "CREATE"). The string is
+   * converted to {@link DrillAccessType}; if the conversion fails the
+   * operator is not a supported access type and access is denied.
+   *
+   * @param user       the username requesting access
+   * @param dataSource the Drill storage plugin name (e.g. "dfs", "hbase")
+   * @param schema     the schema path (e.g. "dfs.tmp")
+   * @param table      the table name
+   * @param operator   the access type string (e.g. "SELECT", "CREATE")
+   * @return {@code true} if access is allowed
+   */
+  public static boolean checkTableAccess(String user, String dataSource, 
String schema,
+      String table, String operator) {
+    DrillAccessType accessType;
+    try {
+      accessType = DrillAccessType.valueOf(operator.toUpperCase());
+    } catch (Exception e) {
+      LOG.error("Unsupported access type '{}', denied table access for 
user={}, schema={}, table={}",

Review Comment:
   When access is denied, DrillAccessControl returns false, and the caller 
throws a UserException.permissionError() with a clear message. 
   For table-level denial (in DrillCalciteCatalogReader):
   `Access denied: user 'alice' lacks SELECT privilege on mysql.shf.orders`
   For column-level denial (in ColumnAccessChecker):
   Access denied: user 'alice' lacks SELECT privilege on one or more columns
   ([order_date]) of table mysql.shf.orders





> Integrate Apache Ranger authorization for Drill
> -----------------------------------------------
>
>                 Key: DRILL-8548
>                 URL: https://issues.apache.org/jira/browse/DRILL-8548
>             Project: Apache Drill
>          Issue Type: New Feature
>          Components:  Server
>    Affects Versions: 1.23.0
>            Reporter: shihuafeng
>            Priority: Major
>             Fix For: 1.23.0
>
>
> This issue introduces Apache Ranger as a pluggable authorization framework 
> for Drill, enabling centralized table-level and column-level access control 
> for Drill queries. It is a substantial feature spanning three layers: a new 
> drill-ranger module , integration hooks in exec/java-exec, and distribution 
> packaging. The design follows Drill's existing AccessAuthorizer SPI and 
> Calcite's RelShuttle mechanism so that column-level checks happen in the 
> toRel phase before physical planning



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to