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 <[email protected]>
Authored: Thu Feb 5 15:33:39 2015 -0800
Committer: Madhan Neethiraj <[email protected]>
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));
+       }
+}

Reply via email to