You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by el...@apache.org on 2017/11/27 22:50:10 UTC

[2/2] hbase git commit: HBASE-19318 Use the PB service interface as the judge of whether some security feature exists

HBASE-19318 Use the PB service interface as the judge of whether some security feature exists

Hard-coded checks on HBase implementations (e.g. AccessController and
VisibilityController) preclude custom implementations of authorization
and visibility labels (Apache Ranger).

Signed-off-by: Ted Yu <te...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/e42d20f8
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/e42d20f8
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/e42d20f8

Branch: refs/heads/branch-2
Commit: e42d20f8ddd4c27d6138e71ec78d0fbfe59790a4
Parents: 96e63ac
Author: Josh Elser <el...@apache.org>
Authored: Wed Nov 22 11:14:40 2017 -0500
Committer: Josh Elser <el...@apache.org>
Committed: Mon Nov 27 17:49:41 2017 -0500

----------------------------------------------------------------------
 .../hadoop/hbase/master/MasterRpcServices.java  |  50 +++++-
 .../master/TestMasterCoprocessorServices.java   | 170 +++++++++++++++++++
 2 files changed, 214 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/e42d20f8/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
index 1a8fd06..ce85b66 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java
@@ -47,6 +47,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
 import org.apache.hadoop.hbase.client.TableState;
 import org.apache.hadoop.hbase.client.VersionInfoUtil;
 import org.apache.hadoop.hbase.client.replication.ReplicationPeerConfigUtil;
+import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
 import org.apache.hadoop.hbase.errorhandling.ForeignException;
 import org.apache.hadoop.hbase.exceptions.UnknownProtocolException;
 import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
@@ -64,6 +65,8 @@ import org.apache.hadoop.hbase.procedure2.LockType;
 import org.apache.hadoop.hbase.procedure2.LockedResource;
 import org.apache.hadoop.hbase.procedure2.Procedure;
 import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService;
 import org.apache.hadoop.hbase.quotas.MasterQuotaManager;
 import org.apache.hadoop.hbase.quotas.QuotaObserverChore;
 import org.apache.hadoop.hbase.quotas.QuotaUtil;
@@ -1768,9 +1771,9 @@ public class MasterRpcServices extends RSRpcServices
       } else {
         capabilities.add(SecurityCapabilitiesResponse.Capability.SIMPLE_AUTHENTICATION);
       }
-      // The AccessController can provide AUTHORIZATION and CELL_AUTHORIZATION
-      if (master.cpHost != null &&
-            master.cpHost.findCoprocessor(AccessController.class.getName()) != null) {
+      // A coprocessor that implements AccessControlService can provide AUTHORIZATION and
+      // CELL_AUTHORIZATION
+      if (master.cpHost != null && hasAccessControlServiceCoprocessor(master.cpHost)) {
         if (AccessController.isAuthorizationSupported(master.getConfiguration())) {
           capabilities.add(SecurityCapabilitiesResponse.Capability.AUTHORIZATION);
         }
@@ -1778,9 +1781,8 @@ public class MasterRpcServices extends RSRpcServices
           capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_AUTHORIZATION);
         }
       }
-      // The VisibilityController can provide CELL_VISIBILITY
-      if (master.cpHost != null &&
-            master.cpHost.findCoprocessor(VisibilityController.class.getName()) != null) {
+      // A coprocessor that implements VisibilityLabelsService can provide CELL_VISIBILITY.
+      if (master.cpHost != null && hasVisibilityLabelsServiceCoprocessor(master.cpHost)) {
         if (VisibilityController.isCellAuthorizationSupported(master.getConfiguration())) {
           capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_VISIBILITY);
         }
@@ -1792,6 +1794,42 @@ public class MasterRpcServices extends RSRpcServices
     return response.build();
   }
 
+  /**
+   * Determines if there is a MasterCoprocessor deployed which implements
+   * {@link org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService.Interface}.
+   */
+  boolean hasAccessControlServiceCoprocessor(MasterCoprocessorHost cpHost) {
+    return checkCoprocessorWithService(
+        cpHost.findCoprocessors(MasterCoprocessor.class), AccessControlService.Interface.class);
+  }
+
+  /**
+   * Determines if there is a MasterCoprocessor deployed which implements
+   * {@link org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService.Interface}.
+   */
+  boolean hasVisibilityLabelsServiceCoprocessor(MasterCoprocessorHost cpHost) {
+    return checkCoprocessorWithService(
+        cpHost.findCoprocessors(MasterCoprocessor.class),
+        VisibilityLabelsService.Interface.class);
+  }
+
+  /**
+   * Determines if there is a coprocessor implementation in the provided argument which extends
+   * or implements the provided {@code service}.
+   */
+  boolean checkCoprocessorWithService(
+      List<MasterCoprocessor> coprocessorsToCheck, Class<?> service) {
+    if (coprocessorsToCheck == null || coprocessorsToCheck.isEmpty()) {
+      return false;
+    }
+    for (MasterCoprocessor cp : coprocessorsToCheck) {
+      if (service.isAssignableFrom(cp.getClass())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   private MasterSwitchType convert(MasterProtos.MasterSwitchType switchType) {
     switch (switchType) {
       case SPLIT:

http://git-wip-us.apache.org/repos/asf/hbase/blob/e42d20f8/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterCoprocessorServices.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterCoprocessorServices.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterCoprocessorServices.java
new file mode 100644
index 0000000..af550ab
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestMasterCoprocessorServices.java
@@ -0,0 +1,170 @@
+/*
+ * 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.hadoop.hbase.master;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.hadoop.hbase.JMXListener;
+import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor;
+import org.apache.hadoop.hbase.coprocessor.MasterObserver;
+import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
+import org.apache.hadoop.hbase.coprocessor.RegionObserver;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsResponse;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GetUserPermissionsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GetUserPermissionsResponse;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GrantRequest;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.GrantResponse;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.RevokeRequest;
+import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.RevokeResponse;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.SetAuthsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
+import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsService;
+import org.apache.hadoop.hbase.security.access.AccessController;
+import org.apache.hadoop.hbase.security.visibility.VisibilityController;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.google.protobuf.RpcCallback;
+import com.google.protobuf.RpcController;
+
+/**
+ * Tests that the MasterRpcServices is correctly searching for implementations of the
+ * Coprocessor Service and not just the "default" implementations of those services.
+ */
+@Category({SmallTests.class})
+public class TestMasterCoprocessorServices {
+  private static class MockAccessController implements AccessControlService.Interface,
+      MasterCoprocessor, RegionCoprocessor, MasterObserver, RegionObserver {
+
+    @Override
+    public void grant(RpcController controller, GrantRequest request,
+        RpcCallback<GrantResponse> done) {}
+
+    @Override
+    public void revoke(RpcController controller, RevokeRequest request,
+        RpcCallback<RevokeResponse> done) {}
+
+    @Override
+    public void getUserPermissions(RpcController controller, GetUserPermissionsRequest request,
+        RpcCallback<GetUserPermissionsResponse> done) {}
+
+    @Override
+    public void checkPermissions(RpcController controller, CheckPermissionsRequest request,
+        RpcCallback<CheckPermissionsResponse> done) {}
+  }
+
+  private static class MockVisibilityController implements VisibilityLabelsService.Interface,
+      MasterCoprocessor, RegionCoprocessor, MasterObserver, RegionObserver {
+
+    @Override
+    public void addLabels(RpcController controller, VisibilityLabelsRequest request,
+        RpcCallback<VisibilityLabelsResponse> done) {
+    }
+
+    @Override
+    public void setAuths(RpcController controller, SetAuthsRequest request,
+        RpcCallback<VisibilityLabelsResponse> done) {
+    }
+
+    @Override
+    public void clearAuths(RpcController controller, SetAuthsRequest request,
+        RpcCallback<VisibilityLabelsResponse> done) {
+    }
+
+    @Override
+    public void getAuths(RpcController controller, GetAuthsRequest request,
+        RpcCallback<GetAuthsResponse> done) {
+    }
+
+    @Override
+    public void listLabels(RpcController controller, ListLabelsRequest request,
+        RpcCallback<ListLabelsResponse> done) {
+    }
+  }
+
+  private MasterRpcServices masterServices;
+
+  @SuppressWarnings("unchecked")
+  @Before
+  public void setup() {
+    masterServices = mock(MasterRpcServices.class);
+    when(masterServices.hasAccessControlServiceCoprocessor(
+        any(MasterCoprocessorHost.class))).thenCallRealMethod();
+    when(masterServices.hasVisibilityLabelsServiceCoprocessor(
+        any(MasterCoprocessorHost.class))).thenCallRealMethod();
+    when(masterServices.checkCoprocessorWithService(
+        any(List.class), any(Class.class))).thenCallRealMethod();
+  }
+
+  @Test
+  public void testAccessControlServices() {
+    MasterCoprocessor defaultImpl = new AccessController();
+    MasterCoprocessor customImpl = new MockAccessController();
+    MasterCoprocessor unrelatedImpl = new JMXListener();
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(defaultImpl), AccessControlService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(customImpl), AccessControlService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        Collections.emptyList(), AccessControlService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        null, AccessControlService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(unrelatedImpl), AccessControlService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Arrays.asList(unrelatedImpl, customImpl), AccessControlService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Arrays.asList(unrelatedImpl, defaultImpl), AccessControlService.Interface.class));
+  }
+
+  @Test
+  public void testVisibilityLabelServices() {
+    MasterCoprocessor defaultImpl = new VisibilityController();
+    MasterCoprocessor customImpl = new MockVisibilityController();
+    MasterCoprocessor unrelatedImpl = new JMXListener();
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(defaultImpl), VisibilityLabelsService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(customImpl), VisibilityLabelsService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        Collections.emptyList(), VisibilityLabelsService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        null, VisibilityLabelsService.Interface.class));
+    assertFalse(masterServices.checkCoprocessorWithService(
+        Collections.singletonList(unrelatedImpl), VisibilityLabelsService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Arrays.asList(unrelatedImpl, customImpl), VisibilityLabelsService.Interface.class));
+    assertTrue(masterServices.checkCoprocessorWithService(
+        Arrays.asList(unrelatedImpl, defaultImpl), VisibilityLabelsService.Interface.class));
+  }
+}