From 449f2953c9c017f5a9a3152ff6faa563d881e617 Mon Sep 17 00:00:00 2001 From: Yulei-Yang Date: Fri, 3 Mar 2023 09:45:08 +0800 Subject: [PATCH] [Improvement](auth)(step-1) add ranger authorizer for hms catalog (#17153) --- fe/fe-core/pom.xml | 17 ++ .../catalog/authorizer/HiveAccessType.java | 23 ++ .../catalog/authorizer/HiveObjectType.java | 22 ++ .../RangerHiveAccessController.java | 190 ++++++++++++ .../RangerHiveAccessControllerFactory.java | 30 ++ .../authorizer/RangerHiveAuditHandler.java | 270 ++++++++++++++++++ .../RangerHiveAuthorizerProvider.java | 49 ++++ .../catalog/authorizer/RangerHivePlugin.java | 27 ++ .../authorizer/RangerHiveResource.java | 96 +++++++ 9 files changed, 724 insertions(+) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java 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. + + + org.apache.ranger + ranger-plugins-common + 2.3.0 + + + ch.qos.logback + logback-classic + + + elasticsearch-rest-high-level-client + org.elasticsearch.client + + + + doris-fe 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 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 hiveResources) throws AuthorizationException { + try { + List requests = new ArrayList<>(); + for (RangerHiveResource resource : hiveResources) { + RangerAccessRequestImpl request = createRequest(currentUser, accessType); + request.setResource(resource); + + requests.add(request); + } + + Collection 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 cols, + PrivPredicate wanted) throws AuthorizationException { + List 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 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 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 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 createAuditEvents(Collection results) { + + Map auditEventsMap = new HashMap<>(); + Iterator 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 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 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) only for this + * condition + */ + @Override + public void processResults(Collection results) { + List 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 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); + } +}