[
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)