This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 449f2953c9 [Improvement](auth)(step-1) add ranger authorizer for hms 
catalog (#17153)
449f2953c9 is described below

commit 449f2953c9c017f5a9a3152ff6faa563d881e617
Author: Yulei-Yang <[email protected]>
AuthorDate: Fri Mar 3 09:45:08 2023 +0800

    [Improvement](auth)(step-1) add ranger authorizer for hms catalog (#17153)
---
 fe/fe-core/pom.xml                                 |  17 ++
 .../doris/catalog/authorizer/HiveAccessType.java   |  23 ++
 .../doris/catalog/authorizer/HiveObjectType.java   |  22 ++
 .../authorizer/RangerHiveAccessController.java     | 190 +++++++++++++++
 .../RangerHiveAccessControllerFactory.java         |  30 +++
 .../catalog/authorizer/RangerHiveAuditHandler.java | 270 +++++++++++++++++++++
 .../authorizer/RangerHiveAuthorizerProvider.java   |  49 ++++
 .../doris/catalog/authorizer/RangerHivePlugin.java |  27 +++
 .../catalog/authorizer/RangerHiveResource.java     |  96 ++++++++
 9 files changed, 724 insertions(+)

diff --git a/fe/fe-core/pom.xml b/fe/fe-core/pom.xml
index 086ed1e92a..15c847f00d 100644
--- a/fe/fe-core/pom.xml
+++ b/fe/fe-core/pom.xml
@@ -820,6 +820,23 @@ under the License.
             </exclusions>
         </dependency>
 
+        <!-- 
https://mvnrepository.com/artifact/org.apache.ranger/ranger-plugins-common -->
+        <dependency>
+            <groupId>org.apache.ranger</groupId>
+            <artifactId>ranger-plugins-common</artifactId>
+            <version>2.3.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>ch.qos.logback</groupId>
+                    <artifactId>logback-classic</artifactId>
+                </exclusion>
+                <exclusion>
+                    
<artifactId>elasticsearch-rest-high-level-client</artifactId>
+                    <groupId>org.elasticsearch.client</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
     </dependencies>
     <build>
         <finalName>doris-fe</finalName>
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java
new file mode 100644
index 0000000000..233f3c463a
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java
@@ -0,0 +1,23 @@
+// 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.doris.catalog.authorizer;
+
+public enum HiveAccessType {
+    NONE, CREATE, ALTER, DROP, INDEX, LOCK, SELECT, UPDATE, USE, READ, WRITE, 
ALL, SERVICEADMIN,
+    TEMPUDFADMIN;
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java
new file mode 100644
index 0000000000..94f4f7e684
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java
@@ -0,0 +1,22 @@
+// 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.doris.catalog.authorizer;
+
+public enum HiveObjectType {
+    NONE, DATABASE, TABLE, VIEW, INDEX, COLUMN, FUNCTION;
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java
new file mode 100644
index 0000000000..2c819334de
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java
@@ -0,0 +1,190 @@
+// 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.doris.catalog.authorizer;
+
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.common.AuthorizationException;
+import org.apache.doris.mysql.privilege.CatalogAccessController;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+
+import 
org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAccessControlException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.policyengine.RangerPolicyEngine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RangerHiveAccessController implements CatalogAccessController {
+    public static final String CLIENT_TYPE_DORIS = "doris";
+    private static final Logger LOG = 
LogManager.getLogger(RangerHiveAccessController.class);
+    private RangerHivePlugin hivePlugin;
+    private RangerHiveAuditHandler auditHandler;
+
+    public RangerHiveAccessController(Map<String, String> properties) {
+        String serviceName = properties.get("ranger.service.name");
+        hivePlugin = new RangerHivePlugin(serviceName);
+        auditHandler = new RangerHiveAuditHandler(hivePlugin.getConfig());
+    }
+
+    private RangerAccessRequestImpl createRequest(UserIdentity currentUser, 
HiveAccessType accessType) {
+        RangerAccessRequestImpl request = new RangerAccessRequestImpl();
+        request.setUser(currentUser.getQualifiedUser());
+        request.setUserRoles(currentUser.getRoles());
+        request.setAction(accessType.name());
+        if (accessType == HiveAccessType.USE) {
+            request.setAccessType(RangerPolicyEngine.ANY_ACCESS);
+        } else {
+            request.setAccessType(accessType.name().toLowerCase());
+        }
+        request.setClientIPAddress(currentUser.getHost());
+        request.setClientType(CLIENT_TYPE_DORIS);
+        request.setAccessTime(new Date());
+
+        return request;
+    }
+
+    private void checkPrivileges(UserIdentity currentUser, HiveAccessType 
accessType,
+                                List<RangerHiveResource> hiveResources) throws 
AuthorizationException {
+        try {
+            List<RangerAccessRequest> requests = new ArrayList<>();
+            for (RangerHiveResource resource : hiveResources) {
+                RangerAccessRequestImpl request = createRequest(currentUser, 
accessType);
+                request.setResource(resource);
+
+                requests.add(request);
+            }
+
+            Collection<RangerAccessResult> results = 
hivePlugin.isAccessAllowed(requests, auditHandler);
+            for (RangerAccessResult result : results) {
+                LOG.debug("match policy:" + result.getPolicyId());
+                if (!result.getIsAllowed()) {
+                    LOG.debug(result.getReason());
+                    throw new AuthorizationException(String.format(
+                        "Permission denied: user [%s] does not have privilege 
for [%s] command on [%s]",
+                        currentUser.getQualifiedUser(), accessType.name(),
+                        
result.getAccessRequest().getResource().getAsString()));
+                }
+            }
+        } finally {
+            auditHandler.flushAudit();
+        }
+    }
+
+    private boolean checkPrivilege(UserIdentity currentUser, HiveAccessType 
accessType,
+                                  RangerHiveResource resource) {
+        RangerAccessRequestImpl request = createRequest(currentUser, 
accessType);
+        request.setResource(resource);
+
+        RangerAccessResult result = hivePlugin.isAccessAllowed(request, 
auditHandler);
+        auditHandler.flushAudit();
+
+        if (result == null) {
+            LOG.warn(String.format("Error getting authorizer result, please 
check your ranger config. Request: %s",
+                    request));
+            return false;
+        }
+
+        if (result.getIsAllowed()) {
+            return true;
+        } else {
+            LOG.debug(String.format(
+                    "Permission denied: user [%s] does not have privilege for 
[%s] command on [%s]",
+                    currentUser.getQualifiedUser(), accessType.name(),
+                    result.getAccessRequest().getResource().getAsString()));
+            return false;
+        }
+    }
+
+    public String getFilterExpr(UserIdentity currentUser, HiveAccessType 
accessType,
+                              RangerHiveResource resource) throws 
HiveAccessControlException {
+        RangerAccessRequestImpl request = createRequest(currentUser, 
accessType);
+        request.setResource(resource);
+        RangerAccessResult result = hivePlugin.isAccessAllowed(request, 
auditHandler);
+        auditHandler.flushAudit();
+
+        return result.getFilterExpr();
+    }
+
+    public void getColumnMask(UserIdentity currentUser, HiveAccessType 
accessType,
+                              RangerHiveResource resource) {
+        RangerAccessRequestImpl request = createRequest(currentUser, 
accessType);
+        request.setResource(resource);
+        RangerAccessResult result = hivePlugin.isAccessAllowed(request, 
auditHandler);
+        auditHandler.flushAudit();
+
+        LOG.debug(String.format("maskType: %s, maskTypeDef: %s, maskedValue: 
%s", result.getMaskType(),
+                result.getMaskTypeDef(), result.getMaskedValue()));
+    }
+
+    public HiveAccessType convertToAccessType(PrivPredicate predicate) {
+        if (predicate == PrivPredicate.SHOW) {
+            return HiveAccessType.USE;
+        } else if (predicate == PrivPredicate.ADMIN) {
+            return HiveAccessType.ALL;
+        } else if (predicate == PrivPredicate.LOAD) {
+            return HiveAccessType.UPDATE;
+        } else if (predicate == PrivPredicate.ALTER) {
+            return HiveAccessType.ALTER;
+        } else if (predicate == PrivPredicate.CREATE) {
+            return HiveAccessType.CREATE;
+        } else if (predicate == PrivPredicate.DROP) {
+            return HiveAccessType.DROP;
+        } else if (predicate == PrivPredicate.SELECT) {
+            return HiveAccessType.SELECT;
+        } else {
+            return HiveAccessType.NONE;
+        }
+    }
+
+    @Override
+    public boolean checkCtlPriv(UserIdentity currentUser, String ctl, 
PrivPredicate wanted) {
+        return false;
+    }
+
+    @Override
+    public boolean checkDbPriv(UserIdentity currentUser, String ctl, String 
db, PrivPredicate wanted) {
+        RangerHiveResource resource = new 
RangerHiveResource(HiveObjectType.DATABASE, db);
+        return checkPrivilege(currentUser, convertToAccessType(wanted), 
resource);
+    }
+
+    @Override
+    public boolean checkTblPriv(UserIdentity currentUser, String ctl, String 
db, String tbl, PrivPredicate wanted) {
+        RangerHiveResource resource = new 
RangerHiveResource(HiveObjectType.TABLE, db, tbl);
+        return checkPrivilege(currentUser, convertToAccessType(wanted), 
resource);
+    }
+
+    @Override
+    public void checkColsPriv(UserIdentity currentUser, String ctl, String db, 
String tbl, Set<String> cols,
+                              PrivPredicate wanted) throws 
AuthorizationException {
+        List<RangerHiveResource> resources = new ArrayList<>();
+        for (String col : cols) {
+            RangerHiveResource resource = new 
RangerHiveResource(HiveObjectType.COLUMN, db, tbl, col);
+            resources.add(resource);
+        }
+
+        checkPrivileges(currentUser, convertToAccessType(wanted), resources);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java
new file mode 100644
index 0000000000..3dc5e1c243
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java
@@ -0,0 +1,30 @@
+// 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.doris.catalog.authorizer;
+
+import org.apache.doris.mysql.privilege.AccessControllerFactory;
+import org.apache.doris.mysql.privilege.CatalogAccessController;
+
+import java.util.Map;
+
+public class RangerHiveAccessControllerFactory implements 
AccessControllerFactory {
+    @Override
+    public CatalogAccessController createAccessController(Map<String, String> 
prop) {
+        return new RangerHiveAccessController(prop);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java
new file mode 100644
index 0000000000..6f68c378b4
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java
@@ -0,0 +1,270 @@
+// 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.doris.catalog.authorizer;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import 
org.apache.hadoop.hive.ql.security.authorization.plugin.HiveOperationType;
+import org.apache.ranger.audit.model.AuthzAuditEvent;
+import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RangerHiveAuditHandler extends RangerDefaultAuditHandler {
+
+    public static final String ACCESS_TYPE_ROWFILTER = "ROW_FILTER";
+    public static final String ACCESS_TYPE_INSERT = "INSERT";
+    public static final String ACCESS_TYPE_UPDATE = "UPDATE";
+    public static final String ACCESS_TYPE_DELETE = "DELETE";
+    public static final String ACCESS_TYPE_TRUNCATE = "TRUNCATE";
+    public static final String ACTION_TYPE_METADATA_OPERATION = "METADATA 
OPERATION";
+    public static final String CONF_AUDIT_QUERY_REQUEST_SIZE = 
"xasecure.audit.solr.limit.query.req.size";
+    public static final int DEFAULT_CONF_AUDIT_QUERY_REQUEST_SIZE = 
Integer.MAX_VALUE;
+    private static final Logger LOG = 
LoggerFactory.getLogger(RangerDefaultAuditHandler.class);
+    private static final Set<String> ROLE_OPS = new HashSet<>();
+
+    static {
+        for (HiveOperationType e : EnumSet.of(HiveOperationType.CREATEROLE, 
HiveOperationType.DROPROLE,
+                HiveOperationType.SHOW_ROLES, 
HiveOperationType.SHOW_ROLE_GRANT, HiveOperationType.SHOW_ROLE_PRINCIPALS,
+                HiveOperationType.GRANT_ROLE, HiveOperationType.REVOKE_ROLE)) {
+            ROLE_OPS.add(e.name());
+        }
+    }
+
+    private final int requestQuerySize;
+    private final Collection<AuthzAuditEvent> auditEvents = new ArrayList<>();
+    private boolean deniedExists = false;
+
+    public RangerHiveAuditHandler() {
+        super();
+        requestQuerySize = DEFAULT_CONF_AUDIT_QUERY_REQUEST_SIZE;
+    }
+
+    public RangerHiveAuditHandler(Configuration config) {
+        super(config);
+
+        int configRequestQuerySize = 
config.getInt(CONF_AUDIT_QUERY_REQUEST_SIZE,
+                DEFAULT_CONF_AUDIT_QUERY_REQUEST_SIZE);
+
+        requestQuerySize = (configRequestQuerySize < 1) ? 
DEFAULT_CONF_AUDIT_QUERY_REQUEST_SIZE :
+            configRequestQuerySize;
+    }
+
+    AuthzAuditEvent createAuditEvent(RangerAccessResult result, String 
accessType, String resourcePath) {
+        RangerAccessRequest request = result.getAccessRequest();
+        RangerAccessResource resource = request.getResource();
+        String resourceType = resource != null ? resource.getLeafName() : null;
+
+        AuthzAuditEvent auditEvent = super.getAuthzEvents(result);
+
+        String resourcePathComputed = resourcePath;
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("requestQuerySize = " + requestQuerySize);
+        }
+        if (StringUtils.isNotBlank(request.getRequestData()) && 
request.getRequestData().length() > requestQuerySize) {
+            auditEvent.setRequestData(request.getRequestData().substring(0, 
requestQuerySize));
+        } else {
+            auditEvent.setRequestData(request.getRequestData());
+        }
+        auditEvent.setAccessType(accessType);
+        auditEvent.setResourcePath(resourcePathComputed);
+        auditEvent.setResourceType("@" + resourceType); // to be consistent 
with earlier release
+
+        if (request instanceof RangerAccessRequestImpl && resource instanceof 
RangerHiveResource) {
+            RangerAccessRequestImpl hiveAccessRequest = 
(RangerAccessRequestImpl) request;
+            RangerHiveResource hiveResource = (RangerHiveResource) resource;
+            String hiveAccessType = hiveAccessRequest.getAccessType();
+
+            if (HiveAccessType.USE.toString().equalsIgnoreCase(hiveAccessType) 
&& hiveResource.getObjectType()
+                    == HiveObjectType.DATABASE && 
StringUtils.isBlank(hiveResource.getDatabase())) {
+                // this should happen only for SHOWDATABASES
+                auditEvent.setTags(null);
+            }
+        }
+
+        return auditEvent;
+    }
+
+    AuthzAuditEvent createAuditEvent(RangerAccessResult result) {
+        final AuthzAuditEvent ret;
+
+        RangerAccessRequest request = result.getAccessRequest();
+        RangerAccessResource resource = request.getResource();
+        String resourcePath = resource != null ? resource.getAsString() : null;
+        int policyType = result.getPolicyType();
+
+        if (policyType == RangerPolicy.POLICY_TYPE_DATAMASK && 
result.isMaskEnabled()) {
+            ret = createAuditEvent(result, result.getMaskType(), resourcePath);
+        } else if (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER) {
+            ret = createAuditEvent(result, ACCESS_TYPE_ROWFILTER, 
resourcePath);
+        } else if (policyType == RangerPolicy.POLICY_TYPE_ACCESS) {
+            String accessType = null;
+
+            if (request instanceof RangerAccessRequestImpl) {
+                RangerAccessRequestImpl hiveRequest = 
(RangerAccessRequestImpl) request;
+
+                accessType = hiveRequest.getAccessType();
+
+                String action = request.getAction();
+                if (ACTION_TYPE_METADATA_OPERATION.equals(action)) {
+                    accessType = ACTION_TYPE_METADATA_OPERATION;
+                } else if 
(HiveAccessType.UPDATE.toString().equalsIgnoreCase(accessType)) {
+                    String commandStr = request.getRequestData();
+                    if (StringUtils.isNotBlank(commandStr)) {
+                        if (StringUtils.startsWithIgnoreCase(commandStr, 
ACCESS_TYPE_INSERT)) {
+                            accessType = ACCESS_TYPE_INSERT;
+                        } else if 
(StringUtils.startsWithIgnoreCase(commandStr, ACCESS_TYPE_UPDATE)) {
+                            accessType = ACCESS_TYPE_UPDATE;
+                        } else if 
(StringUtils.startsWithIgnoreCase(commandStr, ACCESS_TYPE_DELETE)) {
+                            accessType = ACCESS_TYPE_DELETE;
+                        } else if 
(StringUtils.startsWithIgnoreCase(commandStr, ACCESS_TYPE_TRUNCATE)) {
+                            accessType = ACCESS_TYPE_TRUNCATE;
+                        }
+                    }
+                }
+            }
+
+            if (StringUtils.isEmpty(accessType)) {
+                accessType = request.getAccessType();
+            }
+
+            ret = createAuditEvent(result, accessType, resourcePath);
+        } else {
+            ret = null;
+        }
+
+        return ret;
+    }
+
+    List<AuthzAuditEvent> createAuditEvents(Collection<RangerAccessResult> 
results) {
+
+        Map<Long, AuthzAuditEvent> auditEventsMap = new HashMap<>();
+        Iterator<RangerAccessResult> iterator = results.iterator();
+        AuthzAuditEvent deniedAuditEvent = null;
+        while (iterator.hasNext() && deniedAuditEvent == null) {
+            RangerAccessResult result = iterator.next();
+            if (result.getIsAudited()) {
+                if (!result.getIsAllowed()) {
+                    deniedAuditEvent = createAuditEvent(result);
+                } else {
+                    long policyId = result.getPolicyId();
+                    // add this result to existing event by updating column 
values
+                    if (auditEventsMap.containsKey(policyId)) {
+                        AuthzAuditEvent auditEvent = 
auditEventsMap.get(policyId);
+                        RangerAccessRequestImpl request = 
(RangerAccessRequestImpl) result.getAccessRequest();
+                        RangerHiveResource resource = (RangerHiveResource) 
request.getResource();
+                        String resourcePath = auditEvent.getResourcePath() + 
"," + resource.getColumn();
+                        auditEvent.setResourcePath(resourcePath);
+                        Set<String> tags = getTags(request);
+                        if (tags != null) {
+                            auditEvent.getTags().addAll(tags);
+                        }
+                    } else { // new event as this approval was due to a 
different policy.
+                        AuthzAuditEvent auditEvent = createAuditEvent(result);
+
+                        if (auditEvent != null) {
+                            auditEventsMap.put(policyId, auditEvent);
+                        }
+                    }
+                }
+            }
+        }
+        final List<AuthzAuditEvent> result = (deniedAuditEvent == null) ? new 
ArrayList<>(auditEventsMap.values())
+                : Collections.singletonList(deniedAuditEvent);
+
+        return result;
+    }
+
+    @Override
+    public void processResult(RangerAccessResult result) {
+        if (result == null || !result.getIsAudited()) {
+            return;
+        }
+
+        if (skipFilterOperationAuditing(result)) {
+            return;
+        }
+
+        AuthzAuditEvent auditEvent = createAuditEvent(result);
+
+        if (auditEvent != null) {
+            addAuthzAuditEvent(auditEvent);
+        }
+    }
+
+    /**
+     * This method is expected to be called ONLY to process the results for 
multiple-columns in a table.
+     * To ensure this, RangerHiveAccessController should call 
isAccessAllowed(Collection<requests>) only for this
+     * condition
+     */
+    @Override
+    public void processResults(Collection<RangerAccessResult> results) {
+        List<AuthzAuditEvent> result = createAuditEvents(results);
+        for (AuthzAuditEvent auditEvent : result) {
+            addAuthzAuditEvent(auditEvent);
+        }
+    }
+
+    public void flushAudit() {
+        for (AuthzAuditEvent auditEvent : auditEvents) {
+            if (deniedExists && auditEvent.getAccessResult() != 0) { // if 
deny exists, skip logging for allowed results
+                continue;
+            }
+
+            super.logAuthzAudit(auditEvent);
+        }
+    }
+
+    private void addAuthzAuditEvent(AuthzAuditEvent auditEvent) {
+        if (auditEvent != null) {
+            auditEvents.add(auditEvent);
+
+            if (auditEvent.getAccessResult() == 0) {
+                deniedExists = true;
+            }
+        }
+    }
+
+    private boolean skipFilterOperationAuditing(RangerAccessResult result) {
+        boolean ret = false;
+        RangerAccessRequest accessRequest = result.getAccessRequest();
+        if (accessRequest != null) {
+            String action = accessRequest.getAction();
+            if (ACTION_TYPE_METADATA_OPERATION.equals(action) && 
!result.getIsAllowed()) {
+                ret = true;
+            }
+        }
+        return ret;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java
new file mode 100644
index 0000000000..5415496e16
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java
@@ -0,0 +1,49 @@
+// 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.doris.catalog.authorizer;
+
+import java.util.Map;
+
+public class RangerHiveAuthorizerProvider {
+
+    private static volatile Map<String, RangerHivePlugin> hivePluginMap = null;
+
+    /**
+     * if some catalogs use a same ranger hive service, make them share the 
same authorizer plugin
+     *
+     * @param serviceUrl url of ranger admin
+     * @param serviceName name of hive service in ranger admin
+     * @return
+     */
+    public static RangerHivePlugin getHivePlugin(String serviceUrl, String 
serviceName) {
+        String id = serviceUrl + serviceName;
+
+        if (!hivePluginMap.containsKey(id)) {
+            synchronized (RangerHiveAuthorizerProvider.class) {
+                if (!hivePluginMap.containsKey(id)) {
+                    RangerHivePlugin plugin = new RangerHivePlugin(serviceUrl 
+ serviceName);
+                    plugin.init();
+
+                    hivePluginMap.put(id, plugin);
+                }
+            }
+        }
+
+        return hivePluginMap.get(id);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java
new file mode 100644
index 0000000000..b562eee5a9
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java
@@ -0,0 +1,27 @@
+// 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.doris.catalog.authorizer;
+
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+
+public class RangerHivePlugin extends RangerBasePlugin {
+    public RangerHivePlugin(String serviceName) {
+        super(serviceName, null, null);
+        super.init();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java
 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java
new file mode 100644
index 0000000000..51f28b91ff
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java
@@ -0,0 +1,96 @@
+// 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.doris.catalog.authorizer;
+
+import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
+
+public class RangerHiveResource extends RangerAccessResourceImpl {
+    public static final String KEY_DATABASE = "database";
+    public static final String KEY_TABLE = "table";
+    public static final String KEY_UDF = "udf";
+    public static final String KEY_COLUMN = "column";
+    private HiveObjectType objectType;
+
+    //FirstLevelResource => Database
+    //SecondLevelResource => Table or UDF
+    //ThirdLevelResource => column
+    public RangerHiveResource(HiveObjectType objectType, String 
firstLevelResource) {
+        this(objectType, firstLevelResource, null, null);
+    }
+
+    public RangerHiveResource(HiveObjectType objectType, String 
firstLevelResource, String secondLevelResource) {
+        this(objectType, firstLevelResource, secondLevelResource, null);
+    }
+
+    public RangerHiveResource(HiveObjectType objectType, String 
firstLevelResource, String secondLevelResource,
+                              String thirdLevelResource) {
+        this.objectType = objectType;
+
+        // set essential info according to objectType
+        switch (objectType) {
+            case DATABASE:
+                setValue(KEY_DATABASE, firstLevelResource);
+                break;
+
+            case FUNCTION:
+                if (firstLevelResource == null) {
+                    firstLevelResource = "";
+                }
+                setValue(KEY_DATABASE, firstLevelResource);
+                setValue(KEY_UDF, secondLevelResource);
+                break;
+
+            case COLUMN:
+                setValue(KEY_DATABASE, firstLevelResource);
+                setValue(KEY_TABLE, secondLevelResource);
+                setValue(KEY_COLUMN, thirdLevelResource);
+                break;
+
+            case TABLE:
+            case VIEW:
+            case INDEX:
+                setValue(KEY_DATABASE, firstLevelResource);
+                setValue(KEY_TABLE, secondLevelResource);
+                break;
+
+            case NONE:
+            default:
+                break;
+        }
+    }
+
+    public HiveObjectType getObjectType() {
+        return objectType;
+    }
+
+    public String getDatabase() {
+        return (String) getValue(KEY_DATABASE);
+    }
+
+    public String getTable() {
+        return (String) getValue(KEY_TABLE);
+    }
+
+    public String getUdf() {
+        return (String) getValue(KEY_UDF);
+    }
+
+    public String getColumn() {
+        return (String) getValue(KEY_COLUMN);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to