http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthDB.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthDB.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthDB.java new file mode 100644 index 0000000..9f9affd --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthDB.java @@ -0,0 +1,488 @@ +/* + * 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.pdp.hbase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission.Action; +import org.apache.hadoop.hbase.security.access.UserPermission; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.util.StringUtils; +import org.apache.ranger.authorization.hbase.HBaseAccessController; +import org.apache.ranger.pdp.constants.RangerConstants; + +public class HBaseAuthDB implements HBaseAccessController { + + private static final long MAX_CACHE_AUDIT_ENTRIES = 1000L ; + private static final long MAX_CACHE_ENCRYPT_ENTRIES = 1000L ; + + private static final Log LOG = LogFactory.getLog(HBaseAuthDB.class) ; + + private ArrayList<HBaseAuthRules> ruleList = null; + private ArrayList<HBaseAuthRules> globalList = null; + private ArrayList<HBaseAuthRules> tableList = null; + + private ArrayList<String> auditList = null ; + private HashMap<byte[],Boolean> cachedAuditTable = new HashMap<byte[],Boolean>() ; + + private ArrayList<String> encryptList = null ; + + private HashSet<String> encryptTableList = null ; + private HashMap<byte[],Boolean> cachedEncryptedTable = new HashMap<byte[],Boolean>() ; + + + public HBaseAuthDB(ArrayList<HBaseAuthRules> ruleList, ArrayList<String> auditList, ArrayList<String> encryptList) { + + if (LOG.isDebugEnabled()) { + LOG.debug("+Creating HBaseAuthDB is creating with ruleList [" + (ruleList == null ? 0 : ruleList.size()) + "]" ); + } + + this.auditList = auditList; + this.encryptList = encryptList; + + + this.ruleList = new ArrayList<HBaseAuthRules>() ; + this.globalList = new ArrayList<HBaseAuthRules>() ; + this.tableList = new ArrayList<HBaseAuthRules>() ; + + for(HBaseAuthRules rule : ruleList ) { + if (rule.isGlobalRule()) { + this.globalList.add(rule) ; + if (LOG.isDebugEnabled()) { + LOG.debug("RULE:[" + rule + "] is being added as GLOBAL Policy"); + } + } + else if (rule.isTableRule()) { + this.tableList.add(rule) ; + if (LOG.isDebugEnabled()) { + LOG.debug("RULE:[" + rule + "] is being added as Table Policy"); + } + } + else { + this.ruleList.add(rule) ; + if (LOG.isDebugEnabled()) { + LOG.debug("RULE:[" + rule + "] is being added as non-global, non-table Policy"); + } + } + } + + this.encryptTableList = new HashSet<String>() ; + + if (encryptList != null && encryptList.size() > 0) { + for(String encryptKey : encryptList) { + String[] objKeys = encryptKey.split("/") ; + String tableName = objKeys[0] ; + if (! encryptTableList.contains(tableName)) { + encryptTableList.add(tableName) ; + if (LOG.isDebugEnabled()) { + LOG.debug("EncryptionList:[" + tableName + "] is being added encrypted table."); + } + } + } + } + + + } + + + public boolean isAccessAllowed(User user, Action accessAction) { + + + String access = accessAction.toString().toLowerCase() ; + + if (user == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(GLOBAL," + access + ") => [FALSE] as user passed for check was null."); + } + return false ; + } + + + String username = user.getShortName() ; + + String[] groups = user.getGroupNames() ; + + if (LOG.isDebugEnabled()) { + LOG.debug("Init of Global access Verification - [" + access + "] for user [" + username + "], groups: [" + Arrays.toString(groups) + "]"); + } + + for (HBaseAuthRules rule : globalList) { + + if (rule.getAccessType().equals(access)) { + + String authorizedUser = rule.getUser() ; + String authorizedGroup = rule.getGroup(); + + if (authorizedGroup != null) { + if (RangerConstants.PUBLIC_ACCESS_ROLE.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(GLOBAL," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true ; + } + + for (String group : groups) { + if (group.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(GLOBAL," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + + if (authorizedUser != null) { + if (username.equals(authorizedUser)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(GLOBAL," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(GLOBAL," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [FALSE] as it did not match any rules."); + } + + return false; + } + + public boolean isAccessAllowed(User user, byte[] tableName, Action accessAction) { + + + if ( isAccessAllowed(user,accessAction)) { // Check Global Action + return true ; + } + + String tableNameStr = Bytes.toString(tableName) ; + + String access = accessAction.toString().toLowerCase() ; + + if (user == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + tableNameStr + "," + access + ") => [FALSE] as user passed for check was null."); + } + return false ; + } + + String username = user.getShortName() ; + + String[] groups = user.getGroupNames() ; + + if (LOG.isDebugEnabled()) { + LOG.debug("Init of Table access Verification - [" + access + "] for user [" + username + "], groups: [" + Arrays.toString(groups) + "], tableName: [" + tableNameStr + "]"); + } + + for (HBaseAuthRules rule : tableList) { + + if (rule.isTableNameMatched(tableNameStr)) { + if (rule.getAccessType().equals(access)) { + + String authorizedUser = rule.getUser() ; + + String authorizedGroup = rule.getGroup(); + + if (authorizedGroup != null) { + if (RangerConstants.PUBLIC_ACCESS_ROLE.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + tableNameStr + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true ; + } + + for (String group : groups) { + if (group.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + tableNameStr + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + if (authorizedUser != null && username.equals(authorizedUser)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + tableNameStr + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + tableNameStr + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [FALSE] as it did not match any rules."); + } + + return false; + } + + + + + + + public boolean isAccessAllowed(User user, byte[] tableName, byte[] columnFamily, byte[] qualifier, Action accessAction) { + + String FQColName = getFullyQualifiedColumnName(tableName, columnFamily, qualifier) ; + + String access = accessAction.toString().toLowerCase() ; + + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed on HBaseAuthDB: for FQColName [" + FQColName + "]"); + } + + + if (user == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + FQColName + "," + access + ") => [FALSE] as as user passed for check was null."); + } + return false ; + } + + + if (isAccessAllowed(user, accessAction)) { // Check Global Action + return true ; + } + + if (isAccessAllowed(user,tableName, accessAction)) { // Check Table Action + return true; + } + + + String username = user.getShortName() ; + + String[] groups = user.getGroupNames() ; + + if (LOG.isDebugEnabled()) { + LOG.debug("Init of Table access Verification - [" + access + "] for user [" + username + "], groups: [" + Arrays.toString(groups) + "], FQColumnFamily: [" + FQColName + "]"); + } + + for (HBaseAuthRules rule : ruleList) { + + if (rule.isMatched(FQColName)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Rule [" + rule + "] matched [" + FQColName + "]"); + } + if (rule.getAccessType().equals(access)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Access [" + rule.getAccessType() + "] matched [" + access + "]"); + } + String authorizedUser = rule.getUser() ; + + String authorizedGroup = rule.getGroup(); + + if (authorizedGroup != null) { + if (RangerConstants.PUBLIC_ACCESS_ROLE.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + FQColName + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true ; + } + for (String group : groups) { + if (group.equals(authorizedGroup)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + FQColName + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + + if (authorizedUser != null) { + if (username.equals(authorizedUser)) { + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + FQColName + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [TRUE] as matched for rule: " + rule); + } + return true; + } + } + } + else { + if (LOG.isDebugEnabled()) { + LOG.debug("Access [" + rule.getAccessType() + "] DID NOT match [" + access + "]"); + } + } + } + else { + if (LOG.isDebugEnabled()) { + LOG.debug("Rule [" + rule + "] not matched [" + FQColName + "]"); + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("rulecheck(" + FQColName + "," + access + "," + username + "," + StringUtils.arrayToString(groups) + ") => [FALSE] as it did not match any rules."); + } + + return false; + + } + + public boolean isEncrypted(byte[] tableName, byte[] columnFamily, byte[] qualifier) { + String colName = getFullyQualifiedColumnName(tableName, columnFamily, qualifier) ; + for(String encryptable : encryptList) { + if (FilenameUtils.wildcardMatch(colName,encryptable)) { + return true ; + } + } + return false; + } + + public boolean isAudited(byte[] tableName) { + Boolean ret = cachedAuditTable.get(tableName) ; + if (ret == null) { + ret = isAuditedFromTableList(tableName) ; + synchronized(cachedAuditTable) { + if (cachedAuditTable.size() > MAX_CACHE_AUDIT_ENTRIES) { + cachedAuditTable.clear(); + } + cachedAuditTable.put(tableName,ret) ; + } + } + return ret.booleanValue(); + } + + private boolean isAuditedFromTableList(byte[] tableName) { + boolean ret = false ; + String tableNameStr = Bytes.toString(tableName) ; + for(String auditable : auditList) { + if (FilenameUtils.wildcardMatch(tableNameStr,auditable)) { + ret = true ; + break ; + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("isAudited(" + tableNameStr + "):" + ret) ; + } + + return ret; + } + + + public boolean isTableHasEncryptedColumn(byte[] tableName) { + Boolean ret = cachedEncryptedTable.get(tableName) ; + if (ret == null) { + ret = isTableHasEncryptedColumnFromTableList(tableName) ; + synchronized(cachedEncryptedTable) { + if (cachedEncryptedTable.size() > MAX_CACHE_ENCRYPT_ENTRIES) { + cachedEncryptedTable.clear(); + } + cachedEncryptedTable.put(tableName, ret) ; + } + } + return ret.booleanValue() ; + } + + + private boolean isTableHasEncryptedColumnFromTableList(byte[] tableName) + { + boolean ret = false ; + + String tableNameStr = Bytes.toString(tableName) ; + + for(String encryptTable : encryptTableList) { + ret = FilenameUtils.wildcardMatch(tableNameStr, encryptTable) ; + if (ret) { + break ; + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("isTableHasEncryptedColumn(" + tableNameStr + "):" + ret); + } + + return ret ; + } + + + + public static String getFullyQualifiedColumnName(byte[] tableName, byte[] columnFamily, byte[] qualifier) { + StringBuilder sb = new StringBuilder() ; + + sb.append(((tableName != null && tableName.length > 0) ? Bytes.toString(tableName) : "*")) + .append("/") + .append(((columnFamily != null && columnFamily.length > 0) ? Bytes.toString(columnFamily) : "*")) + .append("/") + .append(((qualifier != null && qualifier.length > 0) ? Bytes.toString(qualifier) : "*")) ; + + return sb.toString() ; + } + + public List<UserPermission> getUserPermissions(User user) { + List<UserPermission> ret = new ArrayList<UserPermission>() ; + + if (user != null) { + ArrayList<ArrayList<HBaseAuthRules>> allList = new ArrayList<ArrayList<HBaseAuthRules>>(); + allList.add(globalList) ; + allList.add(tableList) ; + allList.add(ruleList) ; + for(ArrayList<HBaseAuthRules> rList : allList) { + for(HBaseAuthRules rule : rList) { + UserPermission perm = rule.getUserPermission(user) ; + if (perm != null) { + ret.add(perm) ; + } + } + } + } + + return ret ; + } + + public List<UserPermission> getUserPermissions(User user, byte[] tableName) { + + String tableNameStr = Bytes.toString(tableName) ; + + List<UserPermission> ret = new ArrayList<UserPermission>() ; + + if (user != null) { + ArrayList<ArrayList<HBaseAuthRules>> allList = new ArrayList<ArrayList<HBaseAuthRules>>(); + allList.add(globalList) ; + allList.add(tableList) ; + allList.add(ruleList) ; + for(ArrayList<HBaseAuthRules> rList : allList) { + for(HBaseAuthRules rule : rList) { + if (rule.isTableNameMatched(tableNameStr)) { + UserPermission perm = rule.getUserPermission(user) ; + if (perm != null) { + ret.add(perm) ; + } + } + } + } + } + + return ret ; + } + + + +}
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthRules.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthRules.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthRules.java new file mode 100644 index 0000000..ae3980d --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/HBaseAuthRules.java @@ -0,0 +1,134 @@ +/* + * 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.pdp.hbase; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission; +import org.apache.hadoop.hbase.security.access.UserPermission; +import org.apache.ranger.pdp.constants.RangerConstants; + +public class HBaseAuthRules { + private String tableName ; + private String columnGroupName; + private String columnName ; + private String accessType ; + private String group ; + private String user ; + private String fullyQualifiedColumnName ; + + private static final Log LOG = LogFactory.getLog(HBaseAuthRules.class) ; + + public HBaseAuthRules(String tableName, String columnGroupName, String columnName, String accessType, String user, String group) { + this.tableName = tableName; + this.columnGroupName = columnGroupName; + this.columnName = columnName; + if (accessType != null) { + this.accessType = accessType.toLowerCase() ; + } + this.user = user ; + this.group = group; + this.fullyQualifiedColumnName = tableName + "/" + columnGroupName + "/" + columnName ; + } + + public String getTableName() { + return tableName; + } + public String getColumnGroupName() { + return columnGroupName; + } + public String getColumnName() { + return columnName; + } + public String getAccessType() { + return accessType; + } + public String getGroup() { + return group; + } + + public String getUser() { + return user; + } + + @Override + public String toString() { + return "table: " + tableName + ", columnGroup:" + columnGroupName + ", columnName: " + columnName + ", accessType: " + accessType + ", user:" + user + ", group: " + group ; + } + + public boolean isMatched(String FQColName) { + return FQColName.equals(fullyQualifiedColumnName) || FilenameUtils.wildcardMatch(FQColName,fullyQualifiedColumnName) ; + } + + public boolean isGlobalRule() { + return ("*".equals(tableName) && "*".equals(columnGroupName) && "*".equals(columnName)) ; + } + + public boolean isTableRule() { + return ( ("*".equals(columnGroupName) && "*".equals(columnName)) || ("admin".equals(accessType) || "control".equals(accessType)) ) ; + } + + public boolean isTableNameMatched(String tableNameStr) { + boolean ret = (tableNameStr == null) || (tableNameStr.equals(tableName)) || FilenameUtils.wildcardMatch(tableNameStr,tableName) ; + if (LOG.isDebugEnabled()) { + LOG.debug("TableMatched returns (" + tableNameStr + ", rule:" + tableName + ") returns: " + ret ); + } + return ret ; + } + + public UserPermission getUserPermission(User aUser) { + + if (user == null) { + return null ; + } + + Permission.Action action = null ; + + try { + action = Permission.Action.valueOf(accessType.toUpperCase()) ; + } catch (Throwable e) { + return null ; + } + + if (RangerConstants.PUBLIC_ACCESS_ROLE.equals(group)) { + return new UserPermission("public".getBytes(), TableName.valueOf ( tableName ) , columnGroupName.getBytes(), columnName.getBytes(), action) ; + } + + if (user != null) { + if (aUser.getShortName().equals(user)) { + return new UserPermission(("user:(" + aUser.getShortName() + ")").getBytes(), TableName.valueOf( tableName ) , columnGroupName.getBytes(), columnName.getBytes(), action) ; + } + } + + if (group != null) { + for (String ugroups : aUser.getGroupNames()) { + if (ugroups.equals(group)) { + return new UserPermission(("group:(" + ugroups + ")").getBytes(), TableName.valueOf( tableName ) , columnGroupName.getBytes(), columnName.getBytes(), action) ; + } + } + } + + return null; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/RangerAuthorizer.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/RangerAuthorizer.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/RangerAuthorizer.java new file mode 100644 index 0000000..f832cfd --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/RangerAuthorizer.java @@ -0,0 +1,107 @@ +/* + * 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.pdp.hbase; + +import java.util.List; + +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission.Action; +import org.apache.hadoop.hbase.security.access.UserPermission; +import org.apache.ranger.authorization.hbase.HBaseAccessController; + +public class RangerAuthorizer implements HBaseAccessController { + + private HBaseAccessController authDB = URLBasedAuthDB.getInstance(); + + @Override + public boolean isAccessAllowed(User user, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, accessAction); + } else { + return false; + } + } + + @Override + public boolean isAccessAllowed(User user, byte[] tableName, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, tableName, accessAction); + } else { + return false; + } + } + + + @Override + public boolean isAccessAllowed(User user, byte[] tableName, byte[] columnFamily, byte[] qualifier, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, tableName, columnFamily, qualifier, accessAction); + } else { + return false; + } + } + + @Override + public boolean isEncrypted(byte[] tableName, byte[] columnFamily, byte[] qualifier) { + if (authDB != null) { + return authDB.isEncrypted(tableName, columnFamily, qualifier); + } else { + return false; + } + } + + @Override + public boolean isTableHasEncryptedColumn(byte[] tableName) { + if (authDB != null) { + return authDB.isTableHasEncryptedColumn(tableName); + } else { + return false; + } + } + + + @Override + public boolean isAudited(byte[] tableName) { + if (authDB != null) { + return authDB.isAudited(tableName); + } else { + return false; + } + } + + @Override + public List<UserPermission> getUserPermissions(User aUser) { + if (authDB != null) { + return authDB.getUserPermissions(aUser) ; + } else { + return null; + } + } + + @Override + public List<UserPermission> getUserPermissions(User aUser, byte[] aTableName) { + if (authDB != null) { + return authDB.getUserPermissions(aUser, aTableName) ; + } else { + return null; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/URLBasedAuthDB.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/URLBasedAuthDB.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/URLBasedAuthDB.java new file mode 100644 index 0000000..b0e543a --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hbase/URLBasedAuthDB.java @@ -0,0 +1,233 @@ +/* + * 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.pdp.hbase; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission.Action; +import org.apache.hadoop.hbase.security.access.UserPermission; +import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; +import org.apache.ranger.authorization.hbase.HBaseAccessController; +import org.apache.ranger.pdp.config.PolicyChangeListener; +import org.apache.ranger.pdp.config.PolicyRefresher; +import org.apache.ranger.pdp.constants.RangerConstants; +import org.apache.ranger.pdp.model.Policy; +import org.apache.ranger.pdp.model.PolicyContainer; +import org.apache.ranger.pdp.model.RolePermission; + +public class URLBasedAuthDB implements HBaseAccessController, PolicyChangeListener { + + private static final Log LOG = LogFactory.getLog(URLBasedAuthDB.class); + + private HBaseAuthDB authDB = null; + + private static URLBasedAuthDB me = null ; + + private PolicyRefresher refresher = null ; + + public static URLBasedAuthDB getInstance() { + if (me == null) { + synchronized(URLBasedAuthDB.class) { + URLBasedAuthDB temp = me ; + if (temp == null) { + me = new URLBasedAuthDB() ; + me.init() ; + } + } + } + return me ; + } + + + private URLBasedAuthDB() { + String url = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HBASE_POLICYMGR_URL_PROP); + long refreshInMilli = RangerConfiguration.getInstance().getLong( + RangerConstants.RANGER_HBASE_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_PROP, + RangerConstants.RANGER_HBASE_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_DEFAULT); + + String lastStoredFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HBASE_LAST_SAVED_POLICY_FILE_PROP) ; + + String sslConfigFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HBASE_POLICYMGR_SSL_CONFIG_FILE_PROP) ; + refresher = new PolicyRefresher(url, refreshInMilli,sslConfigFileName,lastStoredFileName) ; + + String saveAsFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HBASE_POLICYMGR_URL_SAVE_FILE_PROP) ; + if (saveAsFileName != null) { + refresher.setSaveAsFileName(saveAsFileName) ; + } + + if (lastStoredFileName != null) { + refresher.setLastStoredFileName(lastStoredFileName); + } + } + + private void init() { + refresher.setPolicyChangeListener(this); + } + + public boolean isAccessAllowed(User user, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, accessAction); + } else { + return false; + } + } + + public boolean isAccessAllowed(User user, byte[] tableName, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, tableName, accessAction); + } else { + return false; + } + } + + + public boolean isAccessAllowed(User user, byte[] tableName, byte[] columnFamily, byte[] qualifier, Action accessAction) { + if (authDB != null) { + return authDB.isAccessAllowed(user, tableName, columnFamily, qualifier, accessAction); + } else { + return false; + } + } + + public boolean isEncrypted(byte[] tableName, byte[] columnFamily, byte[] qualifier) { + if (authDB != null) { + return authDB.isEncrypted(tableName, columnFamily, qualifier); + } else { + return false; + } + } + + public boolean isTableHasEncryptedColumn(byte[] tableName) { + if (authDB != null) { + return authDB.isTableHasEncryptedColumn(tableName); + } else { + return false; + } + } + + + public boolean isAudited(byte[] tableName) { + if (authDB != null) { + return authDB.isAudited(tableName); + } else { + return false; + } + } + + public List<UserPermission> getUserPermissions(User aUser) { + if (authDB != null) { + return authDB.getUserPermissions(aUser) ; + } else { + return null; + } + } + + public List<UserPermission> getUserPermissions(User aUser, byte[] aTableName) { + if (authDB != null) { + return authDB.getUserPermissions(aUser, aTableName) ; + } else { + return null; + } + } + + @Override + public void OnPolicyChange(PolicyContainer aPolicyContainer) { + + if (aPolicyContainer == null) { + return ; + } + + ArrayList<HBaseAuthRules> ruleListTemp = new ArrayList<HBaseAuthRules>(); + + HBaseAuthRules globalRule = new HBaseAuthRules(".META.", "*", "*", "read", null, RangerConstants.PUBLIC_ACCESS_ROLE) ; + ruleListTemp.add(globalRule) ; + globalRule = new HBaseAuthRules("-ROOT-", "*", "*", "read", null, RangerConstants.PUBLIC_ACCESS_ROLE) ; + ruleListTemp.add(globalRule) ; + + ArrayList<String> auditListTemp = new ArrayList<String>(); + + ArrayList<String> encryptList = new ArrayList<String>(); + + for(Policy acl : aPolicyContainer.getAcl()) { + + if (! acl.isEnabled()) { + LOG.debug("Diabled acl found [" + acl + "]. Skipping this acl ...") ; + continue ; + } + + for(String table : acl.getTableList()) { + for(String colfamily : acl.getColumnFamilyList()) { + for(String col : acl.getColumnList()) { + if (table == null || table.isEmpty()) { + table = "*" ; + } + if (colfamily == null || colfamily.isEmpty()) { + colfamily = "*" ; + } + if (col == null || col.isEmpty()) { + col = "*" ; + } + + if (acl.getAuditInd() == 1) { + if (!auditListTemp.contains(table)) { + LOG.debug("Adding [" + table + "] to audit list"); + auditListTemp.add(table); + } + } + + if (acl.getEncryptInd() == 1) { + String fqn = table + "/" + colfamily + "/" + col ; + if (!encryptList.contains(fqn)) { + LOG.debug("Adding [" + fqn + "] to encrypt list"); + encryptList.add(fqn); + } + } + + for(RolePermission rp : acl.getPermissions()) { + for (String accessLevel : rp.getAccess() ) { + if (rp.getGroups() != null && rp.getGroups().size() > 0) { + for (String group : rp.getGroups()) { + HBaseAuthRules rule = new HBaseAuthRules(table, colfamily, col, accessLevel, null, group); + LOG.debug("Adding (group) rule: [" + rule + "]") ; + ruleListTemp.add(rule); + } + } + if (rp.getUsers() != null && rp.getUsers().size() > 0) { + for (String user : rp.getUsers()) { + HBaseAuthRules rule = new HBaseAuthRules(table, colfamily, col, accessLevel, user, null); + LOG.debug("Adding (user) rule: [" + rule + "]") ; + ruleListTemp.add(rule); + } + } + } + } + } + } + } + } + HBaseAuthDB authDBTemp = new HBaseAuthDB(ruleListTemp, auditListTemp, encryptList); + authDB = authDBTemp; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/AdminPolicyChecker.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/AdminPolicyChecker.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/AdminPolicyChecker.java new file mode 100644 index 0000000..919a7a1 --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/AdminPolicyChecker.java @@ -0,0 +1,183 @@ +/* + * 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.pdp.hdfs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.apache.commons.io.FilenameUtils; + +public class AdminPolicyChecker { + + private static final String PATH_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst0123456789-_." ; + private static char[] PATH_CHAR_SET = PATH_CHARS.toCharArray() ; + private static int PATH_CHAR_SET_LEN = PATH_CHAR_SET.length ; + + + public static List<String> adminUserList = new ArrayList<String>() ; // "[email protected]" + public static List<String> adminGroupList = new ArrayList<String>() ; + + static { + adminUserList.add("[email protected]") ; + adminGroupList.add("policymgradmin") ; + } + + + public void checkAdminAccessForResource(String selectedResourcePath, boolean isRecursiveFlag, String username) { + + if (adminUserList.contains(username)) { + return ; + } + + List<String> groups = getUserGroupsForUser(username) ; + + if (adminGroupList.contains(groups)) { + + } + + checkAdminAccessForResource(new Path(selectedResourcePath, isRecursiveFlag), username) ; + } + + private void checkAdminAccessForResource(Path resourcePath, String username) { + + List<Path> adminPathList = getAdminPathFromDB(username) ; + + if (!adminPathList.isEmpty()) { + for(Path adminPath : adminPathList ) { + if (adminPath.isMatched(resourcePath)) { + return ; + } + } + } + + throw new SecurityException("User [" + username + "] does not have admin privileges on path [" + resourcePath + "]") ; + + } + + class Path { + String fullPath ; + boolean recursiveFlag ; + + Path(String fullPath, boolean recursiveFlag) { + this.fullPath = fullPath; + this.recursiveFlag = recursiveFlag; + } + + public boolean isMatched(Path resourcePath) { + // Since it is a Regular Expression Compared with Regular Expression + // We will expand the resourcepath to a normalized form and see if it matches with the fullpath using a WildCardMatch + // THIS IS JUST A WORK-AROUND. Need more permanent solution - 11/19/2013 + + String expandedPath = repaceMetaChars(resourcePath) ; + + if (recursiveFlag) { + return URLBasedAuthDB.isRecursiveWildCardMatch(expandedPath, fullPath) ; + } + else { + return FilenameUtils.wildcardMatch(expandedPath, fullPath) ; + } + } + + private String repaceMetaChars(Path regEx) { + + String expandedPath = regEx.fullPath ; + + if (expandedPath.contains("*")) { + String replacement = getRandomString(5,60) ; + expandedPath.replaceAll("\\*", replacement) ; + } + + if (expandedPath.contains("?")) { + String replacement = getRandomString(1,1) ; + expandedPath.replaceAll("\\?", replacement) ; + } + + if (regEx.recursiveFlag) { + int level = getRandomInt(3,10) ; + if (! expandedPath.endsWith("/")) { + expandedPath = expandedPath + "/" ; + } + expandedPath = expandedPath + getRandomString(5,60) ; + + for(int i = 1 ; i < level ; i++) { + expandedPath = expandedPath + "/" + getRandomString(5,60) ; + } + } + return expandedPath ; + } + + + private Random random = new Random() ; + + private String getRandomString(int minLen, int maxLen) { + StringBuilder sb = new StringBuilder() ; + int len = getRandomInt(minLen,maxLen) ; + for(int i = 0 ; i < len ; i++) { + int charIdx = random.nextInt(PATH_CHAR_SET_LEN) ; + sb.append( PATH_CHAR_SET[charIdx] ) ; + } + return null; + } + + private int getRandomInt(int min, int max) { + if (min == max) { + return min ; + } + else { + int interval = max - min ; + return ((random.nextInt() % interval) + min) ; + } + } + + } + + + private List<Path> getAdminPathFromDB(String username) { + + List<Path> ret = new ArrayList<Path>() ; + + // + // TODO: database work to get ACL .... + // + + // Get all policy acl where the user has ADMIN permission + + // Get all policy acl where group associated with user has ADMIN permission + // For each of the acl + // For path in acl.getResourcePath().splitBy(",") + // ret.add(new Path(path, acl.recursiveFlag)) ; + + return ret; + } + + + private List<String> getUserGroupsForUser(String username) { + List<String> groupList = new ArrayList<String>() ; + + // + // TODO: database work to get List of groups .... + // + + return groupList ; + } + + + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/RangerAuthorizer.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/RangerAuthorizer.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/RangerAuthorizer.java new file mode 100644 index 0000000..da6dd65 --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/RangerAuthorizer.java @@ -0,0 +1,40 @@ +/* + * 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.pdp.hdfs; + +import java.util.Set; + +import org.apache.ranger.authorization.hadoop.HDFSAccessVerifier; + +public class RangerAuthorizer implements HDFSAccessVerifier { + + private static URLBasedAuthDB authDB = URLBasedAuthDB.getInstance() ; + + @Override + public boolean isAccessGranted(String aPathName, String aPathOwnerName, String access, String username, Set<String> groups) { + return authDB.isAccessGranted(aPathName, aPathOwnerName, access, username, groups); + } + + @Override + public boolean isAuditLogEnabled(String aPathName) { + return authDB.isAuditLogEnabled(aPathName) ; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/URLBasedAuthDB.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/URLBasedAuthDB.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/URLBasedAuthDB.java new file mode 100644 index 0000000..4136c6d --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hdfs/URLBasedAuthDB.java @@ -0,0 +1,479 @@ +/* + * 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.pdp.hdfs; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.ranger.authorization.hadoop.HDFSAccessVerifier; +import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; +import org.apache.ranger.pdp.config.PolicyChangeListener; +import org.apache.ranger.pdp.config.PolicyRefresher; +import org.apache.ranger.pdp.constants.RangerConstants; +import org.apache.ranger.pdp.model.Policy; +import org.apache.ranger.pdp.model.PolicyContainer; +import org.apache.ranger.pdp.model.ResourcePath; +import org.apache.ranger.pdp.model.RolePermission; + +public class URLBasedAuthDB implements HDFSAccessVerifier, PolicyChangeListener { + + private static final Log LOG = LogFactory.getLog(URLBasedAuthDB.class) ; + + private static URLBasedAuthDB me = null; + + private PolicyRefresher refresher = null ; + + private PolicyContainer policyContainer = null; + + private HashMap<String,Boolean> cachedAuditFlag = new HashMap<String,Boolean>() ; // needs to be cleaned when ruleList changes + + private static final long MAX_NO_OF_AUDIT_CACHE_ENTRIES = 1000L ; + + + public static URLBasedAuthDB getInstance() { + if (me == null) { + synchronized (URLBasedAuthDB.class) { + URLBasedAuthDB temp = me; + if (temp == null) { + me = new URLBasedAuthDB(); + me.init() ; + } + } + } + return me; + } + + private URLBasedAuthDB() { + String url = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HDFS_POLICYMGR_URL_PROP); + long refreshInMilli = RangerConfiguration.getInstance().getLong( + RangerConstants.RANGER_HDFS_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_PROP , + RangerConstants.RANGER_HDFS_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_DEFAULT); + String sslConfigFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HDFS_POLICYMGR_SSL_CONFIG_FILE_PROP) ; + + String lastStoredFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HDFS_LAST_SAVED_POLICY_FILE_PROP) ; + + refresher = new PolicyRefresher(url, refreshInMilli,sslConfigFileName,lastStoredFileName) ; + + String saveAsFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HDFS_POLICYMGR_URL_SAVE_FILE_PROP) ; + if (saveAsFileName != null) { + refresher.setSaveAsFileName(saveAsFileName) ; + } + + if (lastStoredFileName != null) { + refresher.setLastStoredFileName(lastStoredFileName); + } + } + + private void init() { + refresher.setPolicyChangeListener(this); + } + + @Override + public void OnPolicyChange(PolicyContainer aPolicyContainer) { + setPolicyContainer(aPolicyContainer); + } + + + @Override + public boolean isAccessGranted(String aPathName, String pathOwnerName, String access, String username, Set<String> groups) { + + PolicyContainer pc = getPolicyContainer() ; + + if (pc == null) { + return false ; + } + + for(Policy acl : pc.getAcl()) { + + if (! acl.isEnabled()) { + LOG.debug("Diabled acl found [" + acl + "]. Skipping this acl ...") ; + continue ; + } + + for(ResourcePath resource : acl.getResourceList()) { + + String path = resource.getPath() ; + + boolean rulePathMatched = false ; + + if (acl.getRecursiveInd() == 1) { + if (resource.isWildcardPath()) { + rulePathMatched = isRecursiveWildCardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.startsWith(path) ; + } + } + else { + if (resource.isWildcardPath()) { + rulePathMatched = FilenameUtils.wildcardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.equals(path) ; + } + } + + + if (rulePathMatched) { + for (RolePermission rp : acl.getPermissions()) { + if (rp.getAccess().contains(access)) { + if ( rp.getUsers().contains(username) ) { + return true ; + } + for(String ug : groups) { + if ( rp.getGroups().contains(ug)) { + return true ; + } + } + if (rp.getGroups().contains(RangerConstants.PUBLIC_ACCESS_ROLE)) { + return true ; + } + } + } + } + + } + } + + return false ; + + } + + public static boolean isRecursiveWildCardMatch(String pathToCheck, String wildcardPath) { + if (pathToCheck != null) { + StringBuilder sb = new StringBuilder() ; + for(String p : pathToCheck.split(File.separator) ) { + sb.append(p) ; + boolean matchFound = FilenameUtils.wildcardMatch(sb.toString(), wildcardPath) ; + if (matchFound) { + return true ; + } + sb.append(File.separator) ; + } + sb = null; + } + return false; + } + + public PolicyContainer getPolicyContainer() { + return policyContainer; + } + + private synchronized void setPolicyContainer(PolicyContainer aPolicyContainer) { + + for(Policy p : aPolicyContainer.getAcl()) { + for(RolePermission rp : p.getPermissions()) { + List<String> rpaccess = rp.getAccess() ; + if (rpaccess != null && rpaccess.size() > 0) { + List<String> temp = new ArrayList<String>() ; + for(String s : rpaccess) { + temp.add(s.toLowerCase()) ; + } + rp.setAccess(temp); + } + } + } + + this.policyContainer = aPolicyContainer ; + this.cachedAuditFlag.clear(); + } + + + + public UserPermission printPermissionInfo(UserGroupInformation ugi) { + return printPermissionInfo(ugi, null) ; + } + + public UserPermission printPermissionInfo(UserGroupInformation ugi, String aPathName) { + + String username = ugi.getShortUserName() ; + + String[] groups = ugi.getGroupNames() ; + + UserPermission up = new UserPermission(username,groups, aPathName) ; + + PolicyContainer pc = getPolicyContainer() ; + + if (pc != null) { + + for(Policy acl : pc.getAcl()) { + + for(ResourcePath resource : acl.getResourceList()) { + + String path = resource.getPath() ; + + boolean rulePathMatched = false ; + + if (acl.getRecursiveInd() == 1) { + if (resource.isWildcardPath()) { + rulePathMatched = isRecursiveWildCardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.startsWith(path) ; + } + } + else { + if (resource.isWildcardPath()) { + rulePathMatched = FilenameUtils.wildcardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.equals(path) ; + } + } + + + if (rulePathMatched) { + for (RolePermission rp : acl.getPermissions()) { + boolean isAccessGranted = false ; + if (! isAccessGranted ) { + if ( rp.getUsers().contains(username) ) { + up.add(resource, acl.getRecursiveInd(), username, null, rp.getAccess()); + isAccessGranted = true ; + } + } + if ( ! isAccessGranted ) { + for(String ug : groups) { + if ( rp.getGroups().contains(ug)) { + up.add(resource, acl.getRecursiveInd(), null, ug, rp.getAccess()); + } + } + } + if (! isAccessGranted ) { + if (rp.getGroups().contains(RangerConstants.PUBLIC_ACCESS_ROLE)) { + up.add(resource, acl.getRecursiveInd(), null, RangerConstants.PUBLIC_ACCESS_ROLE, rp.getAccess()); + } + } + } + } + } + } + } + + return up ; + } + + + class UserPermission { + + private String userName ; + private String groups ; + private String pathName ; + private HashMap<String,HashSet<String>> userPermissionMap = new HashMap<String,HashSet<String>>() ; + + public UserPermission(String userName, String[] groupList, String pathName) { + this.userName = userName ; + this.pathName = pathName ; + StringBuilder sb = new StringBuilder() ; + boolean first = true ; + TreeSet<String> gl = new TreeSet<String>() ; + for(String g : groupList) { + gl.add(g) ; + } + for(String group : gl) { + if (first) { + first = false ; + } + else { + sb.append(",") ; + } + sb.append(group) ; + } + this.groups = sb.toString() ; + } + + + public void add(ResourcePath resource, int recursiveInd, String userName, String groupName, List<String> accessList) { + + String path = resource.getPath() ; + + if (recursiveInd == 1) { + if (path.endsWith("/")) { + path = path + "**" ; + } + else { + path = path + "/" + "**" ; + } + } + + HashSet<String> permMap = userPermissionMap.get(path) ; + + if (permMap == null) { + permMap = new HashSet<String>() ; + userPermissionMap.put(path,permMap) ; + } + + for(String access : accessList) { + if (! permMap.contains(access)) { + permMap.add(access) ; + } + } + + } + + public void printUserInfo() { + System.out.println("# USER INFORMATION") ; + System.out.println("USER: " + userName ) ; + System.out.println("GROUPS: " + groups ) ; + } + + public void print() { + if (pathName != null) { + System.out.println("# PERMISSION INFORMATION FOR PATH [" + pathName + "]" + (userPermissionMap.size() == 0 ? " - NO RULES FOUND" : "")) ; + } + else { + System.out.println("# PERMISSION INFORMATION" + (userPermissionMap.size() == 0 ? " - NO RULES FOUND" : "")) ; + } + + + if (userPermissionMap.size() > 0) { + TreeSet<String> pathSet = new TreeSet<String>() ; + pathSet.addAll(userPermissionMap.keySet()) ; + StringBuilder sb = new StringBuilder(); + for(String path : pathSet) { + sb.setLength(0) ; + sb.append(String.format("%-50s", path)).append("|") ; + TreeSet<String> permSet = new TreeSet<String>() ; + permSet.addAll(userPermissionMap.get(path)) ; + boolean first = true ; + for(String perm: permSet) { + if (! first) { + sb.append(",") ; + } + else { + first = false ; + } + sb.append(perm) ; + } + System.out.println(sb.toString()) ; + } + } + + } + } + + + @Override + public boolean isAuditLogEnabled(String aPathName) { + boolean ret = false ; + + HashMap<String,Boolean> tempCachedAuditFlag = cachedAuditFlag ; + + Boolean auditResult = (tempCachedAuditFlag == null ? null : tempCachedAuditFlag.get(aPathName)) ; + + if (auditResult != null) { + ret = auditResult ; + } + else { + ret = isAuditLogEnabledByACL(aPathName) ; + if (tempCachedAuditFlag != null) { + // tempCachedAuditFlag.put(aPathName,Boolean.valueOf(ret)) ; + synchronized(tempCachedAuditFlag) { + if (tempCachedAuditFlag.size() > MAX_NO_OF_AUDIT_CACHE_ENTRIES) { + tempCachedAuditFlag.clear(); + } + tempCachedAuditFlag.put(aPathName,Boolean.valueOf(ret)) ; + } + } + } + + return ret ; + + } + + + public boolean isAuditLogEnabledByACL(String aPathName) { + + boolean ret = false ; + + PolicyContainer pc = getPolicyContainer() ; + + if (pc == null) { + return false ; + } + + for(Policy acl : pc.getAcl()) { + + for(ResourcePath resource : acl.getResourceList()) { + + String path = resource.getPath() ; + + boolean rulePathMatched = false ; + + if (acl.getRecursiveInd() == 1) { + if (resource.isWildcardPath()) { + rulePathMatched = isRecursiveWildCardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.startsWith(path) ; + } + } + else { + if (resource.isWildcardPath()) { + rulePathMatched = FilenameUtils.wildcardMatch(aPathName, path) ; + } + else { + rulePathMatched = aPathName.equals(path) ; + } + } + + + if (rulePathMatched) { + ret = ( acl.getAuditInd() == 1) ; + break ; + } + } + } + + return ret ; + } + + public static void main(String[] args) throws Throwable { + LogManager.getLogger(URLBasedAuthDB.class).setLevel(Level.ERROR); + URLBasedAuthDB authDB = URLBasedAuthDB.getInstance() ; + UserPermission up = null; + if (args.length == 0) { + up = authDB.printPermissionInfo(UserGroupInformation.getCurrentUser()); + up.printUserInfo() ; + up.print(); + } + else { + up = authDB.printPermissionInfo(UserGroupInformation.getCurrentUser()); + up.printUserInfo() ; + for(String path : args) { + up = authDB.printPermissionInfo(UserGroupInformation.getCurrentUser(), path); + up.print(); + System.out.println(); + } + } + System.exit(0); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthDB.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthDB.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthDB.java new file mode 100644 index 0000000..f9bdedf --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthDB.java @@ -0,0 +1,306 @@ +/* + * 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.pdp.hive; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo.HiveAccessType; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo.HiveObjectType; +import org.apache.ranger.authorization.utils.StringUtil; + +public class HiveAuthDB { + + private static final Log LOG = LogFactory.getLog(HiveAuthDB.class); + + private ArrayList<HiveAuthRule> allRuleList = null; + private ArrayList<HiveAuthRule> tblRuleList = null; + private ArrayList<HiveAuthRule> colRuleList = null; + + public HiveAuthDB() { + this(null) ; + } + + + public HiveAuthDB(ArrayList<HiveAuthRule> aRuleList) { + + if (aRuleList == null) { + aRuleList = new ArrayList<HiveAuthRule>() ; + } + + LOG.info("Number of Rules in the PolicyContainer: " + ((aRuleList == null) ? 0 : aRuleList.size()) ) ; + + allRuleList = new ArrayList<HiveAuthRule>() ; + colRuleList = new ArrayList<HiveAuthRule>(); + tblRuleList = new ArrayList<HiveAuthRule>() ; + + allRuleList = aRuleList ; + + for (HiveAuthRule rule : aRuleList) { + if (rule.isTableRule()) { + this.tblRuleList.add(rule); + } else { + this.colRuleList.add(rule); + } + } + + } + + public boolean isAccessAllowed(UserGroupInformation ugi, RangerHiveObjectAccessInfo objAccessInfo) { + boolean ret = false; + + if(objAccessInfo.getAccessType() == HiveAccessType.NONE || objAccessInfo.getObjectType() == HiveObjectType.NONE) { + return true; + } + + String accessType = objAccessInfo.getAccessType().name(); + + switch(objAccessInfo.getObjectType()) { + case DATABASE: + ret = isAccessAllowed(ugi, accessType, objAccessInfo.getDatabase()); + break; + + case TABLE: + case INDEX: + case PARTITION: + ret = isAccessAllowed(ugi, accessType, objAccessInfo.getDatabase(), objAccessInfo.getTable()); + break; + + case VIEW: + ret = isAccessAllowed(ugi, accessType, objAccessInfo.getDatabase(), objAccessInfo.getView()); + break; + + case COLUMN: + { + String deniedColumn = findDeniedColumn(ugi, accessType, objAccessInfo.getDatabase(), objAccessInfo.getTable(), objAccessInfo.getColumns()); + + ret = StringUtil.isEmpty(deniedColumn); + + if(! ret) { + objAccessInfo.setDeinedObjectName(RangerHiveObjectAccessInfo.getObjectName(objAccessInfo.getDatabase(), objAccessInfo.getTable(), deniedColumn)); + } + } + break; + + case FUNCTION: + ret = isUDFAccessAllowed(ugi, accessType, objAccessInfo.getDatabase(), objAccessInfo.getFunction()); + break; + + case URI: + // Handled in RangerHiveAuthorizer + break; + + case NONE: + break; + } + + return ret; + } + + public boolean isAudited(RangerHiveObjectAccessInfo objAccessInfo) { + boolean ret = false; + + if( objAccessInfo.getAccessType() == HiveAccessType.NONE + || objAccessInfo.getObjectType() == HiveObjectType.NONE + || objAccessInfo.getObjectType() == HiveObjectType.URI + ) { + return false; + } + + String database = null; + String table = null; + List<String> columns = null; + boolean isUDF = false; + + switch(objAccessInfo.getObjectType()) { + case DATABASE: + database = objAccessInfo.getDatabase(); + break; + + case TABLE: + case INDEX: + case PARTITION: + database = objAccessInfo.getDatabase(); + table = objAccessInfo.getTable(); + break; + + case VIEW: + database = objAccessInfo.getDatabase(); + table = objAccessInfo.getView(); + break; + + case COLUMN: + database = objAccessInfo.getDatabase(); + table = objAccessInfo.getTable(); + columns = objAccessInfo.getColumns(); + break; + + case FUNCTION: + database = objAccessInfo.getDatabase(); + table = objAccessInfo.getFunction(); + isUDF = true; + break; + + case NONE: + case URI: + break; + } + + if(StringUtil.isEmpty(columns)) { + for (HiveAuthRule rule : allRuleList) { + if(isUDF != rule.isUdf()) { + continue; + } + + if (rule.isTableMatch(database, table)) { + ret = rule.isAudited() ; + + if (ret) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAudited(database=" + database + ", table=" + table + ", columns=" + StringUtil.toString(columns) + ") => [" + ret + "] as matched for rule: " + rule); + } + + break ; + } + } + } + } else { + // is audit enabled for any one column being accessed? + for(String colName : columns) { + for (HiveAuthRule rule : allRuleList) { + if(isUDF != rule.isUdf()) { + continue; + } + + ret = rule.isMatched(database, table, colName) && rule.isAudited(); + + if (ret) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAudited(database=" + database + ", table=" + table + ", columns=" + StringUtil.toString(columns) + ") => [" + ret + "] as matched for rule: " + rule); + } + + break ; + } + } + + if(ret) { + break; + } + } + } + + return ret ; + } + + private boolean isAccessAllowed(UserGroupInformation ugi, String accessType, String database) { + boolean ret = false; + + for (HiveAuthRule rule : allRuleList) { + ret = rule.isMatched(database, ugi.getShortUserName(), ugi.getGroupNames(), accessType); + + if(ret) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed(user=" + ugi.getShortUserName() + ", groups=" + StringUtil.toString(ugi.getGroupNames()) + ", accessType=" + accessType + ", database=" + database + ") => [" + ret + "] as matched for rule: " + rule); + } + + break; + } + } + + return ret; + } + + private boolean isAccessAllowed(UserGroupInformation ugi, String accessType, String database, String tableOrView) { + boolean ret = false; + + for (HiveAuthRule rule : tblRuleList) { + ret = rule.isMatched(database, tableOrView, ugi.getShortUserName(), ugi.getGroupNames(), accessType); + + if(ret) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed(user=" + ugi.getShortUserName() + ", groups=" + StringUtil.toString(ugi.getGroupNames()) + ", accessType=" + accessType + ", database=" + database + ", tableOrView=" + tableOrView + ") => [" + ret + "] as matched for rule: " + rule); + } + + break; + } + } + + return ret; + } + + private String findDeniedColumn(UserGroupInformation ugi, String accessType, String database, String tableOrView, List<String> columns) { + String deinedColumn = null; + + boolean isAllowed = isAccessAllowed(ugi, accessType, database, tableOrView); // check if access is allowed at the table level + + if(!isAllowed && !StringUtil.isEmpty(columns)) { + for(String column : columns) { + for (HiveAuthRule rule : colRuleList) { + isAllowed = rule.isMatched(database, tableOrView, column, ugi.getShortUserName(), ugi.getGroupNames(), accessType); + + if(isAllowed) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed(user=" + ugi.getShortUserName() + ", groups=" + StringUtil.toString(ugi.getGroupNames()) + ", accessType=" + accessType + ", database=" + database + ", tableOrView=" + tableOrView + ", column=" + column + ") => [" + isAllowed + "] as matched for rule: " + rule); + } + + break; + } + } + + if(!isAllowed) { + deinedColumn = column; + + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed(user=" + ugi.getShortUserName() + ", groups=" + StringUtil.toString(ugi.getGroupNames()) + ", accessType=" + accessType + ", database=" + database + ", tableOrView=" + tableOrView + ", column=" + column + ") => [" + isAllowed + "]"); + } + break; + } + } + } + + return deinedColumn; + } + + private boolean isUDFAccessAllowed(UserGroupInformation ugi, String accessType, String database, String udfName) { + boolean ret = false; + + for (HiveAuthRule rule : tblRuleList) { + if(! rule.isUdf()) { + continue; + } + + ret = rule.isMatched(database, udfName, ugi.getShortUserName(), ugi.getGroupNames(), accessType); + + if(ret) { + if (LOG.isDebugEnabled()) { + LOG.debug("isAccessAllowed(user=" + ugi.getShortUserName() + ", groups=" + StringUtil.toString(ugi.getGroupNames()) + ", accessType=" + accessType + ", database=" + database + ", udfName=" + udfName + ") => [" + ret + "] as matched for rule: " + rule); + } + + break; + } + } + + return ret; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthRule.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthRule.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthRule.java new file mode 100644 index 0000000..21bd7c1 --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthRule.java @@ -0,0 +1,222 @@ +/* + * 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.pdp.hive; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo.HiveAccessType; +import org.apache.ranger.authorization.hive.constants.RangerHiveConstants; +import org.apache.ranger.authorization.utils.StringUtil; + + +public class HiveAuthRule { + + private static final Log LOG = LogFactory.getLog(HiveAuthRule.class) ; + + public static final String WILDCARD_OBJECT = ".*" ; + + private String databaseName; + private String tableName; + private String columnName; + private String accessType; + private String group; + private String user; + private boolean tableRule = false; + private boolean allGranted = false; + private boolean udf = false; + private boolean tableExcluded = false; + private boolean columnExcluded = false; + private boolean audited = false; + private boolean encrypted = false; + + public HiveAuthRule(String dbName, String tableName, String colName, String permission, String user, String group) { + this(false, dbName,tableName,colName,permission,user,group, false, false) ; + } + + public HiveAuthRule(boolean udfInd, String dbName, String tableName, String colName, String permission, String user, String group, boolean tableExclusionFlag, boolean columnExclusionFlag) { + this.udf = udfInd ; + this.databaseName = StringUtil.toLower(dbName); + this.tableName = StringUtil.toLower(tableName); + this.columnName = StringUtil.toLower(colName); + this.accessType = permission ; + this.user = user; + this.group = group ; + this.tableExcluded = tableExclusionFlag ; + this.columnExcluded = columnExclusionFlag ; + + this.allGranted = StringUtil.equalsIgnoreCase(HiveAccessType.ALL.name(), accessType); + + tableRule = StringUtil.isEmpty(columnName) || WILDCARD_OBJECT.matches(columnName) ; + } + + @Override + public String toString() { + return "db:" + databaseName + ", table: " + tableName + ", columnName: " + columnName + ", accessType: " + accessType + ",user: " + user + ", group: " + group + ",isTable:" + tableRule + ",audited:" + audited + ",encrypted:" + encrypted ; + } + + public boolean isMatched(String user, String[] groups, String accessType) { + String dbName = null; + String tblName = null; + String colName = null; + + return isMatched(dbName, tblName, colName, user, groups, accessType) ; + } + + public boolean isMatched(String dbName, String user, String[] groups, String accessType) { + String tblName = null; + String colName = null; + + return isMatched(dbName, tblName, colName, user, groups, accessType) ; + } + + public boolean isMatched(String dbName, String tblName, String user, String[] groups, String accessType) { + String colName = null; + + return isMatched(dbName, tblName, colName, user, groups, accessType) ; + } + + public boolean isMatched(String dbName, String tblName, String colName, String user, String[] groups, String accessType) { + boolean ret = isMatched(dbName, tblName, colName); + + if(ret) { + // does accessType match? + ret = StringUtil.equalsIgnoreCase(accessType, this.accessType); + + if(! ret && !StringUtil.equalsIgnoreCase(accessType, HiveAccessType.ADMIN.name())) { + ret = this.isAllGranted() || StringUtil.equalsIgnoreCase(accessType, "USE"); + } + + if(ret) { + // does user/group match? + ret = StringUtil.equals(user, this.user) || + StringUtil.equals(RangerHiveConstants.PUBLIC_ACCESS_ROLE, this.group) || + StringUtil.contains(groups, this.group); + } + } + + if(LOG.isDebugEnabled()) { + LOG.debug("isMatched(db=" + dbName + ", table=" + tblName + ", col=" + colName + ", user=" + user + ", groups=" + StringUtil.toString(groups) + ", accessType=" + accessType + ") => rule[" + this.databaseName + ":" + this.tableName + ":" + this.columnName + ":" + this.user + ":" + this.group + ":" + this.accessType + "] returns [" + ret + "]"); + } + + return ret ; + } + + public boolean isMatched(String dbName, String tblName, String colName) { + boolean ret = isTableMatch(dbName, tblName); + + if (ret) { + colName = StringUtil.toLower(colName); + + if (colName != null) { + ret = colName.matches(this.columnName); + + if (columnExcluded) { + ret = (! ret) ; + } + } + } + + if(LOG.isDebugEnabled()) { + LOG.debug("isMatched(db=" + dbName + ", table=" + tblName + ", col=" + colName + ") => rule[" + this.databaseName + ":" + this.tableName + ":" + this.columnName + "] returns [" + ret + "]"); + } + + return ret ; + } + + public boolean isTableMatch(String dbName, String tblName) { + boolean ret = isDBMatch(dbName); + + if(ret) { + tblName = StringUtil.toLower(tblName); + + if(tblName != null) { + ret = tblName.matches(this.tableName); + + if(tableExcluded) { + ret = !ret; + } + } + } + + return ret; + } + + public boolean isDBMatch(String dbName) { + boolean ret = false; + + dbName = StringUtil.toLower(dbName); + + ret = dbName == null || dbName.matches(this.databaseName); + + return ret; + } + + public String getDbName() { + return databaseName; + } + + public String getTableName() { + return tableName; + } + + public String getColumnName() { + return columnName; + } + + public String getAccessType() { + return accessType; + } + + public String getUser() { + return user; + } + + public String getGroup() { + return group; + } + + public boolean isTableRule() { + return tableRule; + } + + public boolean isAllGranted() { + return allGranted ; + } + + public boolean isUdf() { + return udf; + } + + public boolean isAudited() { + return audited; + } + + public void setAudited(boolean audited) { + this.audited = audited; + } + + public boolean isEncrypted() { + return encrypted; + } + + public void setEncrypted(boolean encrypted) { + this.encrypted = encrypted; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthorizationProviderBase.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthorizationProviderBase.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthorizationProviderBase.java new file mode 100644 index 0000000..894d2df --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/HiveAuthorizationProviderBase.java @@ -0,0 +1,64 @@ +/* + * 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.pdp.hive; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hive.ql.metadata.AuthorizationException; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.ranger.authorization.hive.RangerHiveAccessVerifier; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo; + +public class HiveAuthorizationProviderBase implements RangerHiveAccessVerifier { + + private static final Log LOG = LogFactory.getLog(HiveAuthorizationProviderBase.class); + + protected HiveAuthDB authDB = new HiveAuthDB() ; + + + public HiveAuthDB getAuthDB() { + return authDB ; + } + + @Override + public boolean isAccessAllowed(UserGroupInformation ugi, RangerHiveObjectAccessInfo objAccessInfo) { + HiveAuthDB ldb = authDB ; + + if (ldb == null) { + throw new AuthorizationException("No Authorization Agent is available for AuthorizationCheck") ; + } + + boolean ret = ldb.isAccessAllowed(ugi, objAccessInfo); + + return ret; + } + + @Override + public boolean isAudited(RangerHiveObjectAccessInfo objAccessInfo) { + HiveAuthDB ldb = authDB ; + + if (ldb == null) { + throw new AuthorizationException("No Authorization Agent is available for AuthorizationCheck") ; + } + + return ldb.isAudited(objAccessInfo) ; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hive/RangerAuthorizer.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hive/RangerAuthorizer.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/RangerAuthorizer.java new file mode 100644 index 0000000..fc4291c --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/RangerAuthorizer.java @@ -0,0 +1,47 @@ +/* + * 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.pdp.hive; + +import org.apache.hadoop.hive.ql.metadata.AuthorizationException; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.ranger.authorization.hive.RangerHiveAccessVerifier; +import org.apache.ranger.authorization.hive.RangerHiveObjectAccessInfo; + +public class RangerAuthorizer implements RangerHiveAccessVerifier { + + private RangerHiveAccessVerifier authDB = URLBasedAuthDB.getInstance() ; + + + @Override + public boolean isAccessAllowed(UserGroupInformation ugi, RangerHiveObjectAccessInfo objAccessInfo) { + if (authDB == null) { + throw new AuthorizationException("No Authorization Agent is available for AuthorizationCheck") ; + } + return authDB.isAccessAllowed(ugi, objAccessInfo); + } + + @Override + public boolean isAudited(RangerHiveObjectAccessInfo objAccessInfo) { + if (authDB == null) { + throw new AuthorizationException("No Authorization Agent is available for AuthorizationCheck") ; + } + return authDB.isAudited(objAccessInfo) ; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/hive/URLBasedAuthDB.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/hive/URLBasedAuthDB.java b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/URLBasedAuthDB.java new file mode 100644 index 0000000..9de2bf4 --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/hive/URLBasedAuthDB.java @@ -0,0 +1,221 @@ +/* + * 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.pdp.hive; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; +import org.apache.ranger.pdp.config.PolicyChangeListener; +import org.apache.ranger.pdp.config.PolicyRefresher; +import org.apache.ranger.pdp.constants.RangerConstants; +import org.apache.ranger.pdp.model.Policy; +import org.apache.ranger.pdp.model.PolicyContainer; +import org.apache.ranger.pdp.model.RolePermission; + +public class URLBasedAuthDB extends HiveAuthorizationProviderBase implements PolicyChangeListener { + + private static final Log LOG = LogFactory.getLog(URLBasedAuthDB.class) ; + + private static URLBasedAuthDB me = null ; + + private PolicyContainer policyContainer = null ; + + private PolicyRefresher refresher = null ; + + + public static URLBasedAuthDB getInstance() { + if (me == null) { + synchronized(URLBasedAuthDB.class) { + URLBasedAuthDB temp = me ; + if (temp == null) { + me = new URLBasedAuthDB() ; + me.init() ; + } + } + } + return me ; + } + + private URLBasedAuthDB() { + String url = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HIVE_POLICYMGR_URL_PROP); + long refreshInMilli = RangerConfiguration.getInstance().getLong( + RangerConstants.RANGER_HIVE_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_PROP , + RangerConstants.RANGER_HIVE_POLICYMGR_URL_RELOAD_INTERVAL_IN_MILLIS_DEFAULT); + + String lastStoredFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HIVE_LAST_SAVED_POLICY_FILE_PROP) ; + + String sslConfigFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HIVE_POLICYMGR_SSL_CONFIG_FILE_PROP) ; + refresher = new PolicyRefresher(url, refreshInMilli,sslConfigFileName,lastStoredFileName) ; + + String saveAsFileName = RangerConfiguration.getInstance().get(RangerConstants.RANGER_HIVE_POLICYMGR_URL_SAVE_FILE_PROP) ; + if (saveAsFileName != null) { + refresher.setSaveAsFileName(saveAsFileName) ; + } + + if (lastStoredFileName != null) { + refresher.setLastStoredFileName(lastStoredFileName); + } + + } + + private void init() { + refresher.setPolicyChangeListener(this); + } + + public PolicyContainer getPolicyContainer() { + return policyContainer; + } + + @Override + public void OnPolicyChange(PolicyContainer policyContainer) { + + LOG.debug("OnPolicyChange() has been called with new PolicyContainer .....") ; + + try { + + ArrayList<HiveAuthRule> ruleListTemp = new ArrayList<HiveAuthRule>(); + + this.policyContainer = policyContainer; + + if (LOG.isDebugEnabled()) { + LOG.debug("Number of acl found (before isEnabled check): " + ( policyContainer.getAcl() == null ? 0 : policyContainer.getAcl().size() ) ); + } + + for(Policy acl : policyContainer.getAcl()) { + + if (! acl.isEnabled()) { + LOG.debug("Diabled acl found [" + acl + "]. Skipping this acl ...") ; + continue ; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Number of database found in acl [" + acl + "] " + ( acl.getDatabaseList() == null ? 0 : acl.getDatabaseList().size() ) ); + LOG.debug("Number of Tables found in acl [" + acl + "] " + ( acl.getTableList() == null ? 0 : acl.getTableList().size() ) ); + LOG.debug("Number of Columns found in acl [" + acl + "] " + ( acl.getColumnList()== null ? 0 : acl.getColumnList().size() ) ); + } + + boolean isUDF = false ; + + List<String> dbList = new ArrayList<String>() ; + String dbs = replaceFileBasedRegEx(acl.getDatabases()) ; + dbList.add(getRegExFormatted(dbs)) ; + + List<String> tableList = new ArrayList<String>() ; + String udfs = acl.getUdfs() ; + if (udfs != null) { + isUDF = true ; + dbList.clear(); + dbList.add(HiveAuthRule.WILDCARD_OBJECT) ; + tableList.clear(); + udfs = replaceFileBasedRegEx(udfs) ; + tableList.add(getRegExFormatted(udfs)) ; + } + else { + String tables = replaceFileBasedRegEx(acl.getTables()) ; + tableList.add(getRegExFormatted(tables)) ; + } + + List<String> columnList = new ArrayList<String>() ; + String columns = replaceFileBasedRegEx(acl.getColumns()) ; + columnList.add(getRegExFormatted(columns)) ; + + + boolean isAudited = (acl.getAuditInd() == 1) ; + + boolean isEncrypted = (acl.getEncryptInd() == 1) ; + + for(String db : dbList) { + + for(String table : tableList) { + + for(String col : columnList) { + + for(RolePermission rp : acl.getPermissions()) { + for (String accessLevel : rp.getAccess() ) { + for (String group : rp.getGroups()) { + HiveAuthRule rule = new HiveAuthRule(isUDF, db, table, col, accessLevel.toLowerCase(), null, group, acl.isTableSelectionExcluded(), acl.isColumnSelectionExcluded()); + rule.setAudited(isAudited); + rule.setEncrypted(isEncrypted); + LOG.debug("Adding rule [" + rule + "] to the authdb."); + ruleListTemp.add(rule); + } + for (String user : rp.getUsers()) { + HiveAuthRule rule = new HiveAuthRule(isUDF, db, table, col, accessLevel.toLowerCase(), user, null,acl.isTableSelectionExcluded(), acl.isColumnSelectionExcluded()); + rule.setAudited(isAudited); + rule.setEncrypted(isEncrypted); + LOG.debug("Adding rule [" + rule + "] to the authdb."); + ruleListTemp.add(rule); + } + } + } + + + } + } + } + } + HiveAuthDB authDBTemp = new HiveAuthDB(ruleListTemp); + authDB = authDBTemp; + } + catch(Throwable t) { + LOG.error("OnPolicyChange has failed with an exception", t); + } + } + + public static String getRegExFormatted(String userEnteredStr) { + + if (userEnteredStr == null || userEnteredStr.trim().length() == 0) { + return HiveAuthRule.WILDCARD_OBJECT ; + } + + StringBuilder sb = new StringBuilder() ; + + for(String s : userEnteredStr.split(",")) { + if (sb.length() == 0) { + sb.append("(") ; + } + else { + sb.append("|") ; + } + sb.append(s.trim()) ; + } + + if (sb.length() > 0) { + sb.append(")") ; + } + + return sb.toString() ; + } + + + public static String replaceFileBasedRegEx(String userEnteredStr) { + if (userEnteredStr != null) { + userEnteredStr = userEnteredStr.replaceAll("\\.", "\\.") + .replaceAll("\\?", "\\.") + .replaceAll("\\*", ".*") ; + } + return userEnteredStr ; + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/413fcb68/agents-impl/src/main/java/org/apache/ranger/pdp/knox/RangerAuthorizer.java ---------------------------------------------------------------------- diff --git a/agents-impl/src/main/java/org/apache/ranger/pdp/knox/RangerAuthorizer.java b/agents-impl/src/main/java/org/apache/ranger/pdp/knox/RangerAuthorizer.java new file mode 100644 index 0000000..4d14ce0 --- /dev/null +++ b/agents-impl/src/main/java/org/apache/ranger/pdp/knox/RangerAuthorizer.java @@ -0,0 +1,64 @@ +/** + * 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.pdp.knox; + +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.knox.KnoxAccessVerifier; + +public class RangerAuthorizer implements KnoxAccessVerifier { + + private static final Log LOG = LogFactory.getLog(RangerAuthorizer.class) ; + + private static URLBasedAuthDB authDB = URLBasedAuthDB.getInstance() ; + + public RangerAuthorizer() { + } + + @Override + public boolean isAccessAllowed(String topologyName, String serviceName, String accessType, + String userName, Set<String> groups, String requestIp) { + boolean accessAllowed = authDB.isAccessGranted(topologyName, serviceName, accessType, userName, groups, + requestIp); + if (LOG.isDebugEnabled()) { + LOG.debug("Computed access permission for topology: " + topologyName + + ", service: " + serviceName + + ", access: " + accessType + + ", requestingIp: " +requestIp + + ", requestingUser: " + userName + + ", requestingUserGroups: " + groups + + ", permitted: " + accessAllowed); + } + return accessAllowed; + } + + @Override + public boolean isAuditEnabled(String topologyName, String serviceName) { + boolean auditEnabled = authDB.isAuditEnabled(topologyName, serviceName); + if (LOG.isDebugEnabled()) { + LOG.debug("Computed audit enabled for topology: " + topologyName + + ", service: " + serviceName + + ", auditLogEnabled: " + auditEnabled); + } + return auditEnabled; + } + +}
