This is an automated email from the ASF dual-hosted git repository.
stoty pushed a commit to branch branch-3
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-3 by this push:
new 0aadea9d71b HBASE-28952 Add coprocessor hook to authorize user based
on client SSL certificate chain (#6447)
0aadea9d71b is described below
commit 0aadea9d71b987e29674550217740c8fbe5137d4
Author: Andor Molnár <[email protected]>
AuthorDate: Fri Jan 17 02:09:23 2025 -0600
HBASE-28952 Add coprocessor hook to authorize user based on client SSL
certificate chain (#6447)
Signed-off-by: Bryan Beaudreault <[email protected]>
(cherry picked from commit d477bf163bf51d38e6596131f6608da6f9597f95)
---
.../hadoop/hbase/coprocessor/CoprocessorHost.java | 1 +
.../hadoop/hbase/coprocessor/RpcCoprocessor.java | 32 ++++++
.../coprocessor/RpcCoprocessorEnvironment.java | 28 +++++
.../hadoop/hbase/coprocessor/RpcObserver.java | 74 +++++++++++++
.../hadoop/hbase/ipc/RpcCoprocessorHost.java | 122 +++++++++++++++++++++
.../org/apache/hadoop/hbase/ipc/RpcServer.java | 22 ++++
.../hadoop/hbase/ipc/RpcServerInterface.java | 2 +
.../hadoop/hbase/ipc/ServerRpcConnection.java | 3 +
.../hbase/coprocessor/TestRpcCoprocessor.java | 109 ++++++++++++++++++
9 files changed, 393 insertions(+)
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
index c1ba9e274ad..137fe3b061d 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
@@ -62,6 +62,7 @@ public abstract class CoprocessorHost<C extends Coprocessor,
E extends Coprocess
"hbase.coprocessor.user.region.classes";
public static final String MASTER_COPROCESSOR_CONF_KEY =
"hbase.coprocessor.master.classes";
public static final String WAL_COPROCESSOR_CONF_KEY =
"hbase.coprocessor.wal.classes";
+ public static final String RPC_COPROCESSOR_CONF_KEY =
"hbase.coprocessor.rpc.classes";
public static final String ABORT_ON_ERROR_KEY =
"hbase.coprocessor.abortonerror";
public static final boolean DEFAULT_ABORT_ON_ERROR = true;
public static final String COPROCESSORS_ENABLED_CONF_KEY =
"hbase.coprocessor.enabled";
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessor.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessor.java
new file mode 100644
index 00000000000..108e023cfa8
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.coprocessor;
+
+import java.util.Optional;
+import org.apache.hadoop.hbase.Coprocessor;
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+
[email protected](HBaseInterfaceAudience.COPROC)
[email protected]
+public interface RpcCoprocessor extends Coprocessor {
+ default Optional<RpcObserver> getRpcObserver() {
+ return Optional.empty();
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessorEnvironment.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessorEnvironment.java
new file mode 100644
index 00000000000..2f3b2c8e67c
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcCoprocessorEnvironment.java
@@ -0,0 +1,28 @@
+/*
+ * 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.coprocessor;
+
+import org.apache.hadoop.hbase.CoprocessorEnvironment;
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+
[email protected](HBaseInterfaceAudience.COPROC)
[email protected]
+public interface RpcCoprocessorEnvironment extends
CoprocessorEnvironment<RpcCoprocessor> {
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcObserver.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcObserver.java
new file mode 100644
index 00000000000..4bc13c17515
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RpcObserver.java
@@ -0,0 +1,74 @@
+/*
+ * 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.coprocessor;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.apache.yetus.audience.InterfaceStability;
+
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos;
+
+/**
+ * Coprocessors implement this interface to observe and mediate RPC events in
Master and RS
+ * instances.
+ * <p>
+ * Since most implementations will be interested in only a subset of hooks,
this class uses
+ * 'default' functions to avoid having to add unnecessary overrides. When the
functions are
+ * non-empty, it's simply to satisfy the compiler by returning value of
expected (non-void) type. It
+ * is done in a way that these default definitions act as no-op. So our
suggestion to implementation
+ * would be to not call these 'default' methods from overrides.
+ * <p>
+ * <h3>Exception Handling</h3><br>
+ * For all functions, exception handling is done as follows:
+ * <ul>
+ * <li>Exceptions of type {@link IOException} are reported back to client.</li>
+ * <li>For any other kind of exception:
+ * <ul>
+ * <li>Be aware that this coprocessor doesn't support abortion. If the
configuration
+ * {@link CoprocessorHost#ABORT_ON_ERROR_KEY} is set to true, the event will
be logged, but the RPC
+ * server won't be aborted.</li>
+ * <li>Otherwise, coprocessor is removed from the server.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ */
[email protected](HBaseInterfaceAudience.COPROC)
[email protected]
+public interface RpcObserver {
+
+ /**
+ * Called before authorizing connection
+ * @param ctx the coprocessor instance's environment
+ */
+ default void
preAuthorizeConnection(ObserverContext<RpcCoprocessorEnvironment> ctx,
+ RPCProtos.ConnectionHeader connectionHeader, InetAddress remoteAddr)
throws IOException {
+ }
+
+ /**
+ * Called after successfully authorizing connection
+ * @param ctx the coprocessor instance's environment
+ * @param userName the user name
+ * @param clientCertificateChain list of peer certificates from SSL
connection
+ */
+ default void
postAuthorizeConnection(ObserverContext<RpcCoprocessorEnvironment> ctx,
+ String userName, X509Certificate[] clientCertificateChain) throws
IOException {
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCoprocessorHost.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCoprocessorHost.java
new file mode 100644
index 00000000000..2ced2311989
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCoprocessorHost.java
@@ -0,0 +1,122 @@
+/*
+ * 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.ipc;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.coprocessor.BaseEnvironment;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.coprocessor.RpcCoprocessor;
+import org.apache.hadoop.hbase.coprocessor.RpcCoprocessorEnvironment;
+import org.apache.hadoop.hbase.coprocessor.RpcObserver;
+import org.apache.hadoop.hbase.security.User;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos;
+
[email protected]
+public class RpcCoprocessorHost extends CoprocessorHost<RpcCoprocessor,
RpcCoprocessorEnvironment> {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(RpcCoprocessorHost.class);
+
+ private static class RpcEnvironment extends BaseEnvironment<RpcCoprocessor>
+ implements RpcCoprocessorEnvironment {
+
+ public RpcEnvironment(RpcCoprocessor impl, int priority, int seq,
Configuration conf) {
+ super(impl, priority, seq, conf);
+ }
+ }
+
+ public RpcCoprocessorHost(final Configuration conf) {
+ // RPCServer cannot be aborted, so we don't pass Abortable down here.
+ super(null);
+ this.conf = conf;
+ boolean coprocessorsEnabled =
+ conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
DEFAULT_COPROCESSORS_ENABLED);
+ LOG.trace("System coprocessor loading is {}", (coprocessorsEnabled ?
"enabled" : "disabled"));
+ loadSystemCoprocessors(conf, RPC_COPROCESSOR_CONF_KEY);
+ }
+
+ @Override
+ public RpcCoprocessorEnvironment createEnvironment(RpcCoprocessor instance,
int priority,
+ int sequence, Configuration conf) {
+ return new RpcEnvironment(instance, priority, sequence, conf);
+ }
+
+ @Override
+ public RpcCoprocessor checkAndGetInstance(Class<?> implClass)
+ throws InstantiationException, IllegalAccessException {
+ try {
+ if (RpcCoprocessor.class.isAssignableFrom(implClass)) {
+ return
implClass.asSubclass(RpcCoprocessor.class).getDeclaredConstructor().newInstance();
+ } else {
+ LOG.error("{} is not of type RpcCoprocessor. Check the configuration
of {}",
+ implClass.getName(), CoprocessorHost.RPC_COPROCESSOR_CONF_KEY);
+ return null;
+ }
+ } catch (NoSuchMethodException | InvocationTargetException e) {
+ throw (InstantiationException) new
InstantiationException(implClass.getName()).initCause(e);
+ }
+ }
+
+ private final ObserverGetter<RpcCoprocessor, RpcObserver> rpcObserverGetter =
+ RpcCoprocessor::getRpcObserver;
+
+ abstract class RpcObserverOperation extends
ObserverOperationWithoutResult<RpcObserver> {
+ public RpcObserverOperation() {
+ super(rpcObserverGetter);
+ }
+
+ public RpcObserverOperation(boolean bypassable) {
+ this(null, bypassable);
+ }
+
+ public RpcObserverOperation(User user) {
+ super(rpcObserverGetter, user);
+ }
+
+ public RpcObserverOperation(User user, boolean bypassable) {
+ super(rpcObserverGetter, user, bypassable);
+ }
+ }
+
+ public void preAuthorizeConnection(RPCProtos.ConnectionHeader
connectionHeader,
+ InetAddress remoteAddr) throws IOException {
+ execOperation(coprocEnvironments.isEmpty() ? null : new
RpcObserverOperation() {
+ @Override
+ protected void call(RpcObserver observer) throws IOException {
+ observer.preAuthorizeConnection(this, connectionHeader, remoteAddr);
+ }
+ });
+ }
+
+ public void postAuthorizeConnection(final String userName,
+ final X509Certificate[] clientCertificates) throws IOException {
+ execOperation(coprocEnvironments.isEmpty() ? null : new
RpcObserverOperation() {
+ @Override
+ protected void call(RpcObserver observer) throws IOException {
+ observer.postAuthorizeConnection(this, userName, clientCertificates);
+ }
+ });
+ }
+}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java
index 4ff1a0b5482..fce51f36014 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServer.java
@@ -42,6 +42,7 @@ import org.apache.hadoop.hbase.ExtendedCellScanner;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.Server;
import org.apache.hadoop.hbase.conf.ConfigurationObserver;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
import org.apache.hadoop.hbase.io.ByteBuffAllocator;
import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
import org.apache.hadoop.hbase.monitoring.TaskMonitor;
@@ -54,6 +55,7 @@ import
org.apache.hadoop.hbase.security.SaslUtil.QualityOfProtection;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.security.UserProvider;
import org.apache.hadoop.hbase.security.token.AuthenticationTokenSecretManager;
+import org.apache.hadoop.hbase.util.CoprocessorConfigurationUtil;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.GsonUtil;
import org.apache.hadoop.hbase.util.Pair;
@@ -220,6 +222,8 @@ public abstract class RpcServer implements
RpcServerInterface, ConfigurationObse
protected volatile boolean allowFallbackToSimpleAuth;
+ volatile RpcCoprocessorHost cpHost;
+
/**
* Used to get details for scan with a scanner_id<br/>
* TODO try to figure out a better way and remove reference from
regionserver package later.
@@ -312,6 +316,8 @@ public abstract class RpcServer implements
RpcServerInterface, ConfigurationObse
this.isOnlineLogProviderEnabled = getIsOnlineLogProviderEnabled(conf);
this.scheduler = scheduler;
+
+ initializeCoprocessorHost(getConf());
}
@Override
@@ -324,6 +330,13 @@ public abstract class RpcServer implements
RpcServerInterface, ConfigurationObse
refreshAuthManager(newConf, new HBasePolicyProvider());
}
refreshSlowLogConfiguration(newConf);
+ if (
+ CoprocessorConfigurationUtil.checkConfigurationChange(getConf(), newConf,
+ CoprocessorHost.RPC_COPROCESSOR_CONF_KEY)
+ ) {
+ LOG.info("Update the RPC coprocessor(s) because the configuration has
changed");
+ initializeCoprocessorHost(newConf);
+ }
}
private void refreshSlowLogConfiguration(Configuration newConf) {
@@ -898,4 +911,13 @@ public abstract class RpcServer implements
RpcServerInterface, ConfigurationObse
public List<BlockingServiceAndInterface> getServices() {
return services;
}
+
+ private void initializeCoprocessorHost(Configuration conf) {
+ this.cpHost = new RpcCoprocessorHost(conf);
+ }
+
+ @Override
+ public RpcCoprocessorHost getRpcCoprocessorHost() {
+ return cpHost;
+ }
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServerInterface.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServerInterface.java
index 9bf5fc3817d..a2df50118ad 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServerInterface.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcServerInterface.java
@@ -85,4 +85,6 @@ public interface RpcServerInterface {
*/
void setNamedQueueRecorder(final NamedQueueRecorder namedQueueRecorder);
+ /** Return RPC's instance of {@link RpcCoprocessorHost} */
+ RpcCoprocessorHost getRpcCoprocessorHost();
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java
index c17a8da9041..dcf64b14276 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java
@@ -366,6 +366,7 @@ abstract class ServerRpcConnection implements Closeable {
processConnectionHeader(buf);
callCleanupIfNeeded();
this.connectionHeaderRead = true;
+
this.rpcServer.getRpcCoprocessorHost().preAuthorizeConnection(connectionHeader,
addr);
if (rpcServer.needAuthorization() && !authorizeConnection()) {
// Throw FatalConnectionException wrapping ACE so client does right
thing and closes
// down the connection instead of trying to read non-existent retun.
@@ -373,6 +374,8 @@ abstract class ServerRpcConnection implements Closeable {
+ connectionHeader.getServiceName() + " is unauthorized for user: "
+ ugi);
}
this.user = this.rpcServer.userProvider.create(this.ugi);
+ this.rpcServer.getRpcCoprocessorHost().postAuthorizeConnection(
+ this.user != null ? this.user.getName() : null,
this.clientCertificateChain);
}
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRpcCoprocessor.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRpcCoprocessor.java
new file mode 100644
index 00000000000..5ba3e819918
--- /dev/null
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRpcCoprocessor.java
@@ -0,0 +1,109 @@
+/*
+ * 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.coprocessor;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HBaseTestingUtil;
+import org.apache.hadoop.hbase.ipc.RpcCoprocessorHost;
+import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos;
+
+@Category({ CoprocessorTests.class, MediumTests.class })
+public class TestRpcCoprocessor {
+
+ @ClassRule
+ public static final HBaseClassTestRule CLASS_RULE =
+ HBaseClassTestRule.forClass(TestRpcCoprocessor.class);
+
+ public static class AuthorizationRpcObserver implements RpcCoprocessor,
RpcObserver {
+ final AtomicInteger ctPostAuthorization = new AtomicInteger(0);
+ final AtomicInteger ctPreAuthorization = new AtomicInteger(0);
+ String userName = null;
+
+ @Override
+ public Optional<RpcObserver> getRpcObserver() {
+ return Optional.of(this);
+ }
+
+ @Override
+ public void
preAuthorizeConnection(ObserverContext<RpcCoprocessorEnvironment> ctx,
+ RPCProtos.ConnectionHeader connectionHeader, InetAddress remoteAddr)
throws IOException {
+ ctPreAuthorization.incrementAndGet();
+ }
+
+ @Override
+ public void
postAuthorizeConnection(ObserverContext<RpcCoprocessorEnvironment> ctx,
+ String userName, X509Certificate[] clientCertificateChain) throws
IOException {
+ ctPostAuthorization.incrementAndGet();
+ this.userName = userName;
+ }
+ }
+
+ private static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
+
+ @BeforeClass
+ public static void setupBeforeClass() throws Exception {
+ // set configure to indicate which cp should be loaded
+ Configuration conf = TEST_UTIL.getConfiguration();
+ conf.set(CoprocessorHost.RPC_COPROCESSOR_CONF_KEY,
AuthorizationRpcObserver.class.getName());
+
TEST_UTIL.getConfiguration().setBoolean(CoprocessorHost.ABORT_ON_ERROR_KEY,
false);
+ TEST_UTIL.startMiniCluster();
+ }
+
+ @AfterClass
+ public static void teardownAfterClass() throws Exception {
+ TEST_UTIL.shutdownMiniCluster();
+ }
+
+ @Test
+ public void testHooksCalledFromMaster() {
+ RpcCoprocessorHost coprocHostMaster =
+
TEST_UTIL.getMiniHBaseCluster().getMaster().getRpcServer().getRpcCoprocessorHost();
+ AuthorizationRpcObserver observer =
+ coprocHostMaster.findCoprocessor(AuthorizationRpcObserver.class);
+ assertEquals(2, observer.ctPreAuthorization.get());
+ assertEquals(2, observer.ctPostAuthorization.get());
+ assertEquals(System.getProperty("user.name"), observer.userName);
+ }
+
+ @Test
+ public void testHooksCalledFromRegionServer() {
+ RpcCoprocessorHost coprocHostRs =
+
TEST_UTIL.getMiniHBaseCluster().getRegionServer(0).getRpcServer().getRpcCoprocessorHost();
+ AuthorizationRpcObserver observer =
+ coprocHostRs.findCoprocessor(AuthorizationRpcObserver.class);
+ assertEquals(3, observer.ctPreAuthorization.get());
+ assertEquals(3, observer.ctPostAuthorization.get());
+ assertEquals(System.getProperty("user.name"), observer.userName);
+ }
+}