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 <tedyu@apache.org>
This commit is contained in:
Josh Elser 2017-11-22 11:14:40 -05:00
parent f886716617
commit 5c1acf4792
2 changed files with 214 additions and 6 deletions

View File

@ -47,6 +47,7 @@ import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.client.VersionInfoUtil; import org.apache.hadoop.hbase.client.VersionInfoUtil;
import org.apache.hadoop.hbase.client.replication.ReplicationPeerConfigUtil; 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.errorhandling.ForeignException;
import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; import org.apache.hadoop.hbase.exceptions.UnknownProtocolException;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; 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.LockedResource;
import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.Procedure;
import org.apache.hadoop.hbase.procedure2.ProcedureUtil; 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.MasterQuotaManager;
import org.apache.hadoop.hbase.quotas.QuotaObserverChore; import org.apache.hadoop.hbase.quotas.QuotaObserverChore;
import org.apache.hadoop.hbase.quotas.QuotaUtil; import org.apache.hadoop.hbase.quotas.QuotaUtil;
@ -1768,9 +1771,9 @@ public class MasterRpcServices extends RSRpcServices
} else { } else {
capabilities.add(SecurityCapabilitiesResponse.Capability.SIMPLE_AUTHENTICATION); capabilities.add(SecurityCapabilitiesResponse.Capability.SIMPLE_AUTHENTICATION);
} }
// The AccessController can provide AUTHORIZATION and CELL_AUTHORIZATION // A coprocessor that implements AccessControlService can provide AUTHORIZATION and
if (master.cpHost != null && // CELL_AUTHORIZATION
master.cpHost.findCoprocessor(AccessController.class.getName()) != null) { if (master.cpHost != null && hasAccessControlServiceCoprocessor(master.cpHost)) {
if (AccessController.isAuthorizationSupported(master.getConfiguration())) { if (AccessController.isAuthorizationSupported(master.getConfiguration())) {
capabilities.add(SecurityCapabilitiesResponse.Capability.AUTHORIZATION); capabilities.add(SecurityCapabilitiesResponse.Capability.AUTHORIZATION);
} }
@ -1778,9 +1781,8 @@ public class MasterRpcServices extends RSRpcServices
capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_AUTHORIZATION); capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_AUTHORIZATION);
} }
} }
// The VisibilityController can provide CELL_VISIBILITY // A coprocessor that implements VisibilityLabelsService can provide CELL_VISIBILITY.
if (master.cpHost != null && if (master.cpHost != null && hasVisibilityLabelsServiceCoprocessor(master.cpHost)) {
master.cpHost.findCoprocessor(VisibilityController.class.getName()) != null) {
if (VisibilityController.isCellAuthorizationSupported(master.getConfiguration())) { if (VisibilityController.isCellAuthorizationSupported(master.getConfiguration())) {
capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_VISIBILITY); capabilities.add(SecurityCapabilitiesResponse.Capability.CELL_VISIBILITY);
} }
@ -1792,6 +1794,42 @@ public class MasterRpcServices extends RSRpcServices
return response.build(); 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) { private MasterSwitchType convert(MasterProtos.MasterSwitchType switchType) {
switch (switchType) { switch (switchType) {
case SPLIT: case SPLIT:

View File

@ -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));
}
}