You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2015/02/06 00:35:56 UTC
incubator-ranger git commit: RANGER-230: HBase plugin updates to: 1)
fix generation of duplicate audit messages 2) fix issues in
column-family/column filtering logic.
Repository: incubator-ranger
Updated Branches:
refs/heads/stack 1f0dccadf -> 5c4b59af7
RANGER-230: HBase plugin updates to: 1) fix generation of duplicate audit messages 2) fix issues in column-family/column filtering logic.
Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/5c4b59af
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/5c4b59af
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/5c4b59af
Branch: refs/heads/stack
Commit: 5c4b59af7e5f7ea9382d18566ff2835235f63375
Parents: 1f0dcca
Author: Alok Lal <al...@hortonworks.com>
Authored: Thu Feb 5 15:33:39 2015 -0800
Committer: Madhan Neethiraj <ma...@apache.org>
Committed: Thu Feb 5 15:33:39 2015 -0800
----------------------------------------------------------------------
.../hbase/AuthorizationSession.java | 5 +-
.../hbase/HbaseAuditHandlerImpl.java | 3 +-
.../hbase/RangerAuthorizationCoprocessor.java | 25 +++--
.../hbase/RangerAuthorizationFilter.java | 37 +++++--
.../hbase/RangerAuthorizationFilterTest.java | 108 +++++++++++++++++++
5 files changed, 159 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/5c4b59af/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java
index e6067ce..977c745 100644
--- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java
+++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/AuthorizationSession.java
@@ -163,14 +163,15 @@ public class AuthorizationSession {
resource.setValue("column", _column);
String user = _userUtils.getUserAsString(_user);
- LOG.debug("AuthorizationSession buildRequest: user[" + user + "], groups[" + _groups + "]");
-
RangerAccessRequestImpl request = new RangerAccessRequestImpl(resource, _access, user, _groups);
request.setAction(_operation);
request.setRequestData(_otherInformation);
request.setClientIPAddress(_remoteAddress);
_request = request;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Built request: " + request.toString());
+ }
return this;
}
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/5c4b59af/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/HbaseAuditHandlerImpl.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/HbaseAuditHandlerImpl.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/HbaseAuditHandlerImpl.java
index a5d3f16..fb4f8a0 100644
--- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/HbaseAuditHandlerImpl.java
+++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/HbaseAuditHandlerImpl.java
@@ -41,7 +41,8 @@ public class HbaseAuditHandlerImpl extends RangerDefaultAuditHandler implements
_allEvents.add(_mostRecentEvent);
}
_mostRecentEvent = event;
- return event;
+ // We return null because we don't want default audit handler to audit anything!
+ return null;
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/5c4b59af/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java
index 1a956d3..2e128ec 100644
--- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java
+++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java
@@ -295,7 +295,7 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
if (LOG.isDebugEnabled()) {
final String format = "evaluateAccess: entered: Operation[%s], access[%s], families[%s]";
Map<String, Set<String>> families = getColumnFamilies(familyMap);
- String message = String.format(format, operation, access, families);
+ String message = String.format(format, operation, access, families.toString());
LOG.debug(message);
}
@@ -314,7 +314,7 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
result = new ColumnFamilyAccessResult(true, true, null, null, null, null);
if (LOG.isDebugEnabled()) {
Map<String, Set<String>> families = getColumnFamilies(familyMap);
- String message = String.format(messageTemplate, operation, access, families, result.toString());
+ String message = String.format(messageTemplate, operation, access, families.toString(), result.toString());
LOG.debug(message);
}
return result;
@@ -331,8 +331,11 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
.access(access)
.table(table);
Map<String, Set<String>> families = getColumnFamilies(familyMap);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("evaluateAccess: families to process: " + families.toString());
+ }
if (families == null || families.isEmpty()) {
- // table level access is desired
+ LOG.debug("evaluateAccess: Null or empty families collection, ok. Table level access is desired");
session.buildRequest()
.authorize();
boolean authorized = session.isAuthorized();
@@ -347,10 +350,12 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
authorized ? Collections.singletonList(event) : null,
authorized ? null : event, null, reason);
if (LOG.isDebugEnabled()) {
- String message = String.format(messageTemplate, operation, access, families, result.toString());
+ String message = String.format(messageTemplate, operation, access, families.toString(), result.toString());
LOG.debug(message);
}
return result;
+ } else {
+ LOG.debug("evaluateAccess: Families collection not null. Skipping table-level check, will do finer level check");
}
boolean everythingIsAccessible = true;
@@ -365,10 +370,12 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
for (Map.Entry<String, Set<String>> anEntry : families.entrySet()) {
String family = anEntry.getKey();
session.columnFamily(family);
-
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("evaluateAccess: Processing family: " + family);
+ }
Set<String> columns = anEntry.getValue();
if (columns == null || columns.isEmpty()) {
- // family level access is desired.
+ LOG.debug("evaluateAccess: columns collection null or empty, ok. Family level access is desired.");
session.column(null) // zap stale column from prior iteration of this loop, if any
.buildRequest()
.authorize();
@@ -387,8 +394,12 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
denialReason = String.format("Insufficient permissions for user ‘%s',action: %s, tableName:%s, family:%s, no columns found.", user.getName(), operation, table, family);
}
} else {
+ LOG.debug("evaluateAccess: columns collection not empty. Skipping Family level check, will do finer level access check.");
Set<String> accessibleColumns = new HashSet<String>(); // will be used in to populate our results cache for the filter
for (String column : columns) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("evaluateAccess: Processing column: " + column);
+ }
session.column(column)
.buildRequest()
.authorize();
@@ -414,7 +425,7 @@ public class RangerAuthorizationCoprocessor extends RangerAuthorizationCoprocess
result = new ColumnFamilyAccessResult(everythingIsAccessible, somethingIsAccessible, authorizedEvents, deniedEvent, accessResultsCache, denialReason);
if (LOG.isDebugEnabled()) {
- String message = String.format(messageTemplate, operation, access, families, result.toString());
+ String message = String.format(messageTemplate, operation, access, families.toString(), result.toString());
LOG.debug(message);
}
return result;
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/5c4b59af/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilter.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilter.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilter.java
index 5a66eb2..ae61a1e 100644
--- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilter.java
+++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilter.java
@@ -23,12 +23,16 @@ import java.io.IOException;
import java.util.Map;
import java.util.Set;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.filter.FilterBase;
import org.apache.hadoop.hbase.util.Bytes;
public class RangerAuthorizationFilter extends FilterBase {
+ private static final Log LOG = LogFactory.getLog(RangerAuthorizationFilter.class.getName());
final Map<String, Set<String>> _cache;
public RangerAuthorizationFilter(Map<String, Set<String>> cache) {
@@ -38,31 +42,46 @@ public class RangerAuthorizationFilter extends FilterBase {
@SuppressWarnings("deprecation")
@Override
public ReturnCode filterKeyValue(Cell kv) throws IOException {
- // if our cache is null or empty then there is no hope for any access
+
if (_cache == null || _cache.isEmpty()) {
+ LOG.debug("filterKeyValue: if cache is null or empty then there is no hope for any access. Denied!");
return ReturnCode.NEXT_COL;
}
- // null/empty families are denied
+
byte[] familyBytes = kv.getFamily();
if (familyBytes == null || familyBytes.length == 0) {
+ LOG.debug("filterKeyValue: null/empty families in request! Denied!");
return ReturnCode.NEXT_COL;
}
String family = Bytes.toString(familyBytes);
- // null/empty columns are also denied
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("filterKeyValue: Evaluating family[" + family + "]");
+ }
+
+ if (!_cache.containsKey(family)) {
+ LOG.debug("filterKeyValue: Cache map did not contain the family, i.e. nothing in family has access! Denied!");
+ return ReturnCode.NEXT_COL;
+ }
+ Set<String> columns = _cache.get(family);
+
+ if (CollectionUtils.isEmpty(columns)) {
+ LOG.debug("filterKeyValue: empty/null column set in cache for family implies family level access. No need to bother with column level. Allowed!");
+ return ReturnCode.INCLUDE;
+ }
byte[] columnBytes = kv.getQualifier();
if (columnBytes == null || columnBytes.length == 0) {
+ LOG.debug("filterKeyValue: empty/null column set in request implies family level access, which isn't available. Denied!");
return ReturnCode.NEXT_COL;
}
String column = Bytes.toString(columnBytes);
- // allow if cache contains the family/column in it
- Set<String> columns = _cache.get(family);
- if (columns == null || columns.isEmpty()) {
- // column family with a null/empty set of columns means all columns within that family are allowed
- return ReturnCode.INCLUDE;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("filterKeyValue: Evaluating column[" + column + "]");
}
- else if (columns.contains(column)) {
+ if (columns.contains(column)) {
+ LOG.debug("filterKeyValue: cache contains Column in column-family's collection. Access allowed!");
return ReturnCode.INCLUDE;
} else {
+ LOG.debug("filterKeyValue: cache missing Column in column-family's collection. Access denied!");
return ReturnCode.NEXT_COL;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/5c4b59af/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java
----------------------------------------------------------------------
diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java
new file mode 100644
index 0000000..5575ea7
--- /dev/null
+++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationFilterTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.authorization.hbase;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.filter.Filter.ReturnCode;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+@SuppressWarnings("deprecation")
+public class RangerAuthorizationFilterTest {
+
+ @Test
+ public void testFilterKeyValueCell_happyPath() throws IOException {
+
+ // null/empty column collection in cache for a family implies family level access
+ Map<String, Set<String>> cache = new HashMap<String, Set<String>>();
+ cache.put("family1", Collections.<String> emptySet());
+ RangerAuthorizationFilter filter = new RangerAuthorizationFilter(cache);
+
+ Cell aCell = mock(Cell.class);
+ when(aCell.getFamily()).thenReturn("family1".getBytes());
+ when(aCell.getQualifier()).thenReturn("column1".getBytes());
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+
+ when(aCell.getQualifier()).thenReturn("column2".getBytes());
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+
+ // null empty column collection in REQUEST implies family level access
+ when(aCell.getQualifier()).thenReturn(null);
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+
+ // specific columns in cache should be allowed only if there is a match of family and column
+ cache.clear();
+ cache.put("family1", Sets.newHashSet("column11", "column12"));
+ cache.put("family2", Sets.newHashSet("column21", "column22"));
+ filter = new RangerAuthorizationFilter(cache);
+
+ when(aCell.getFamily()).thenReturn("family1".getBytes());
+ when(aCell.getQualifier()).thenReturn("column11".getBytes());
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+ when(aCell.getQualifier()).thenReturn("column12".getBytes());
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+ when(aCell.getQualifier()).thenReturn("column13".getBytes());
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+
+ when(aCell.getFamily()).thenReturn("family2".getBytes());
+ when(aCell.getQualifier()).thenReturn("column22".getBytes());
+ assertEquals(ReturnCode.INCLUDE, filter.filterKeyValue(aCell));
+
+ when(aCell.getFamily()).thenReturn("family3".getBytes());
+ when(aCell.getQualifier()).thenReturn("column11".getBytes());
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+
+ // asking for family level access when one doesn't exist (colum collection for a family is not null/empty then it should get denied
+ when(aCell.getFamily()).thenReturn("family1".getBytes());
+ when(aCell.getQualifier()).thenReturn(null);
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+ }
+
+ @Test
+ public void testFilterKeyValueCell_firewalling() throws IOException {
+ // null cache will deny everything.
+ RangerAuthorizationFilter filter = new RangerAuthorizationFilter(null);
+ Cell aCell = mock(Cell.class);
+ when(aCell.getFamily()).thenReturn("family1".getBytes());
+ when(aCell.getQualifier()).thenReturn("column1".getBytes());
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+
+ // non-null but empty cache should do the same
+ Map<String, Set<String>> cache = new HashMap<String, Set<String>>();
+ filter = new RangerAuthorizationFilter(cache);
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+
+ // Null family in request would get denied, too
+ when(aCell.getFamily()).thenReturn(null);
+ when(aCell.getQualifier()).thenReturn("column1".getBytes());
+ assertEquals(ReturnCode.NEXT_COL, filter.filterKeyValue(aCell));
+ }
+}