diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java index 8d26328ef00..98d59076254 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserver.java @@ -62,14 +62,14 @@ public abstract class BaseRegionObserver implements RegionObserver { public void stop(CoprocessorEnvironment e) throws IOException { } @Override - public void preOpen(ObserverContext e) { } + public void preOpen(ObserverContext e) throws IOException { } @Override public void postOpen(ObserverContext e) { } @Override - public void preClose(ObserverContext e, - boolean abortRequested) { } + public void preClose(ObserverContext c, boolean abortRequested) + throws IOException { } @Override public void postClose(ObserverContext e, 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 276a88136f2..e0e90ba3b71 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 @@ -64,6 +64,8 @@ import java.util.jar.JarFile; public abstract class CoprocessorHost { public static final String REGION_COPROCESSOR_CONF_KEY = "hbase.coprocessor.region.classes"; + public static final String REGIONSERVER_COPROCESSOR_CONF_KEY = + "hbase.coprocessor.regionserver.classes"; public static final String USER_REGION_COPROCESSOR_CONF_KEY = "hbase.coprocessor.user.region.classes"; public static final String MASTER_COPROCESSOR_CONF_KEY = diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java index 397ba3b0c37..08342410dc8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java @@ -58,8 +58,9 @@ public interface RegionObserver extends Coprocessor { /** * Called before the region is reported as open to the master. * @param c the environment provided by the region server + * @throws IOException if an error occurred on the coprocessor */ - void preOpen(final ObserverContext c); + void preOpen(final ObserverContext c) throws IOException; /** * Called after the region is reported as open to the master. @@ -261,9 +262,10 @@ public interface RegionObserver extends Coprocessor { * Called before the region is reported as closed to the master. * @param c the environment provided by the region server * @param abortRequested true if the region server is aborting + * @throws IOException */ void preClose(final ObserverContext c, - boolean abortRequested); + boolean abortRequested) throws IOException; /** * Called after the region is reported as closed to the master. diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java new file mode 100644 index 00000000000..527df450ae7 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java @@ -0,0 +1,31 @@ +/** + * + * 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.regionserver.RegionServerServices; + +public interface RegionServerCoprocessorEnvironment extends CoprocessorEnvironment { + /** + * Gets the region server services. + * + * @return the region server services + */ + RegionServerServices getRegionServerServices(); +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerObserver.java new file mode 100644 index 00000000000..23ba33cdce3 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerObserver.java @@ -0,0 +1,35 @@ +/** + * + * 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 org.apache.hadoop.hbase.Coprocessor; + +public interface RegionServerObserver extends Coprocessor { + + /** + * Called before stopping region server. + * @param env An instance of RegionServerCoprocessorEnvironment + * @throws IOException Signals that an I/O exception has occurred. + */ + void preStopRegionServer( + final ObserverContext env) + throws IOException; +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index bcde848d167..98c1c3677aa 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -432,6 +432,8 @@ public class HRegionServer implements ClientProtocol, * The reference to the QosFunction */ private final QosFunction qosFunction; + + private RegionServerCoprocessorHost rsHost; /** * Starts a HRegionServer at the default location @@ -517,6 +519,7 @@ public class HRegionServer implements ClientProtocol, "hbase.regionserver.kerberos.principal", this.isa.getHostName()); regionServerAccounting = new RegionServerAccounting(); cacheConfig = new CacheConfig(conf); + this.rsHost = new RegionServerCoprocessorHost(this, this.conf); } /** @@ -1574,10 +1577,15 @@ public class HRegionServer implements ClientProtocol, @Override public void stop(final String msg) { - this.stopped = true; - LOG.info("STOPPED: " + msg); - // Wakes run() if it is sleeping - sleeper.skipSleepCycle(); + try { + this.rsHost.preStop(msg); + this.stopped = true; + LOG.info("STOPPED: " + msg); + // Wakes run() if it is sleeping + sleeper.skipSleepCycle(); + } catch (IOException exp) { + LOG.warn("The region server did not stop", exp); + } } public void waitForServerOnline(){ @@ -2097,6 +2105,10 @@ public class HRegionServer implements ClientProtocol, public ZooKeeperWatcher getZooKeeperWatcher() { return this.zooKeeper; } + + public RegionServerCoprocessorHost getCoprocessorHost(){ + return this.rsHost; + } public ConcurrentSkipListMap getRegionsInTransitionInRS() { @@ -2398,6 +2410,17 @@ public class HRegionServer implements ClientProtocol, */ protected boolean closeRegion(HRegionInfo region, final boolean abort, final boolean zk, final int versionOfClosingNode, ServerName sn) { + //Check for permissions to close. + HRegion actualRegion = this.getFromOnlineRegions(region.getEncodedName()); + if ((actualRegion != null) && (actualRegion.getCoprocessorHost() != null)) { + try { + actualRegion.getCoprocessorHost().preClose(false); + } catch (IOException exp) { + LOG.warn("Unable to close region", exp); + return false; + } + } + if (this.regionsInTransitionInRS.containsKey(region.getEncodedNameAsBytes())) { LOG.warn("Received close for region we are already opening or closing; " + region.getEncodedName()); @@ -3380,6 +3403,10 @@ public class HRegionServer implements ClientProtocol, checkIfRegionInTransition(region.getEncodedNameAsBytes(), OPEN); HRegion onlineRegion = getFromOnlineRegions(region.getEncodedName()); if (null != onlineRegion) { + //Check if the region can actually be opened. + if( onlineRegion.getCoprocessorHost() != null){ + onlineRegion.getCoprocessorHost().preOpen(); + } // See HBASE-5094. Cross check with META if still this RS is owning // the region. Pair p = MetaReader.getRegion( @@ -3459,6 +3486,10 @@ public class HRegionServer implements ClientProtocol, String encodedRegionName = ProtobufUtil.getRegionEncodedName(request.getRegion()); byte[] encodedName = Bytes.toBytes(encodedRegionName); + HRegion region = getRegionByEncodedName(encodedRegionName); + if(region.getCoprocessorHost() != null){ + region.getCoprocessorHost().preClose(false); + } Boolean openAction = regionsInTransitionInRS.get(encodedName); if (openAction != null) { if (openAction.booleanValue()) { @@ -3466,7 +3497,7 @@ public class HRegionServer implements ClientProtocol, } checkIfRegionInTransition(encodedName, CLOSE); } - HRegion region = getRegionByEncodedName(encodedRegionName); + requestCount.increment(); LOG.info("Received close region: " + region.getRegionNameAsString() + ". Version of ZK closing node:" + versionOfClosingNode + diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java index 5069da66636..41244499356 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java @@ -36,7 +36,6 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.Coprocessor; import org.apache.hadoop.hbase.CoprocessorEnvironment; -import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; @@ -53,8 +52,8 @@ import org.apache.hadoop.hbase.coprocessor.CoprocessorService; import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.RegionObserver; -import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.filter.ByteArrayComparable; +import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.ipc.CoprocessorProtocol; import org.apache.hadoop.hbase.regionserver.wal.HLogKey; @@ -267,17 +266,19 @@ public class RegionCoprocessorHost } /** - * Invoked before a region open + * Invoked before a region open. + * + * @throws IOException Signals that an I/O exception has occurred. */ - public void preOpen() { + public void preOpen() throws IOException { ObserverContext ctx = null; for (RegionEnvironment env: coprocessors) { if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { - ((RegionObserver)env.getInstance()).preOpen(ctx); + ((RegionObserver) env.getInstance()).preOpen(ctx); } catch (Throwable e) { - handleCoprocessorThrowableNoRethrow(env, e); + handleCoprocessorThrowable(env, e); } if (ctx.shouldComplete()) { break; @@ -295,7 +296,7 @@ public class RegionCoprocessorHost if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { - ((RegionObserver)env.getInstance()).postOpen(ctx); + ((RegionObserver) env.getInstance()).postOpen(ctx); } catch (Throwable e) { handleCoprocessorThrowableNoRethrow(env, e); } @@ -310,15 +311,15 @@ public class RegionCoprocessorHost * Invoked before a region is closed * @param abortRequested true if the server is aborting */ - public void preClose(boolean abortRequested) { + public void preClose(boolean abortRequested) throws IOException { ObserverContext ctx = null; for (RegionEnvironment env: coprocessors) { if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { - ((RegionObserver)env.getInstance()).preClose(ctx, abortRequested); + ((RegionObserver) env.getInstance()).preClose(ctx, abortRequested); } catch (Throwable e) { - handleCoprocessorThrowableNoRethrow(env, e); + handleCoprocessorThrowable(env, e); } } } @@ -334,7 +335,7 @@ public class RegionCoprocessorHost if (env.getInstance() instanceof RegionObserver) { ctx = ObserverContext.createAndPrepare(env, ctx); try { - ((RegionObserver)env.getInstance()).postClose(ctx, abortRequested); + ((RegionObserver) env.getInstance()).postClose(ctx, abortRequested); } catch (Throwable e) { handleCoprocessorThrowableNoRethrow(env, e); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java new file mode 100644 index 00000000000..66f3f34d664 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.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.hadoop.hbase.regionserver; + +import java.io.IOException; +import java.util.Comparator; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Coprocessor; +import org.apache.hadoop.hbase.CoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.RegionServerObserver; + +public class RegionServerCoprocessorHost extends + CoprocessorHost { + + private RegionServerServices rsServices; + + public RegionServerCoprocessorHost(RegionServerServices rsServices, + Configuration conf) { + this.rsServices = rsServices; + this.conf = conf; + // load system default cp's from configuration. + loadSystemCoprocessors(conf, REGIONSERVER_COPROCESSOR_CONF_KEY); + } + + @Override + public RegionServerEnvironment createEnvironment(Class implClass, + Coprocessor instance, int priority, int sequence, Configuration conf) { + return new RegionServerEnvironment(implClass, instance, priority, + sequence, conf, this.rsServices); + } + + public void preStop(String message) throws IOException { + ObserverContext ctx = null; + for (RegionServerEnvironment env : coprocessors) { + if (env.getInstance() instanceof RegionServerObserver) { + ctx = ObserverContext.createAndPrepare(env, ctx); + ((RegionServerObserver) env.getInstance()).preStopRegionServer(ctx); + if (ctx.shouldComplete()) { + break; + } + } + } + } + + /** + * Coprocessor environment extension providing access to region server + * related services. + */ + static class RegionServerEnvironment extends CoprocessorHost.Environment + implements RegionServerCoprocessorEnvironment { + + private RegionServerServices regionServerServices; + + public RegionServerEnvironment(final Class implClass, + final Coprocessor impl, final int priority, final int seq, + final Configuration conf, final RegionServerServices services) { + super(impl, priority, seq, conf); + this.regionServerServices = services; + } + + @Override + public RegionServerServices getRegionServerServices() { + return regionServerServices; + } + } + + /** + * Environment priority comparator. Coprocessors are chained in sorted + * order. + */ + static class EnvironmentPriorityComparator implements + Comparator { + public int compare(final CoprocessorEnvironment env1, + final CoprocessorEnvironment env2) { + if (env1.getPriority() < env2.getPriority()) { + return -1; + } else if (env1.getPriority() > env2.getPriority()) { + return 1; + } + if (env1.getLoadSequence() < env2.getLoadSequence()) { + return -1; + } else if (env1.getLoadSequence() > env2.getLoadSequence()) { + return 1; + } + return 0; + } + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index 73d091a8e78..f795e755606 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -64,6 +64,7 @@ import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.Permission.Action; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; @@ -105,7 +106,7 @@ import static org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.Acc *

*/ public class AccessController extends BaseRegionObserver - implements MasterObserver, AccessControllerProtocol, + implements MasterObserver, RegionServerObserver, AccessControllerProtocol, AccessControlService.Interface, CoprocessorService { /** * Represents the result of an authorization check for logging and error @@ -514,17 +515,31 @@ public class AccessController extends BaseRegionObserver /* ---- MasterObserver implementation ---- */ public void start(CoprocessorEnvironment env) throws IOException { - // if running on HMaster + + ZooKeeperWatcher zk = null; if (env instanceof MasterCoprocessorEnvironment) { - MasterCoprocessorEnvironment e = (MasterCoprocessorEnvironment)env; - this.authManager = TableAuthManager.get( - e.getMasterServices().getZooKeeper(), - e.getConfiguration()); + // if running on HMaster + MasterCoprocessorEnvironment mEnv = (MasterCoprocessorEnvironment) env; + zk = mEnv.getMasterServices().getZooKeeper(); + } else if (env instanceof RegionServerCoprocessorEnvironment) { + RegionServerCoprocessorEnvironment rsEnv = (RegionServerCoprocessorEnvironment) env; + zk = rsEnv.getRegionServerServices().getZooKeeper(); + } else if (env instanceof RegionCoprocessorEnvironment) { + // if running at region + regionEnv = (RegionCoprocessorEnvironment) env; + zk = regionEnv.getRegionServerServices().getZooKeeper(); } - // if running at region - if (env instanceof RegionCoprocessorEnvironment) { - regionEnv = (RegionCoprocessorEnvironment)env; + // If zk is null or IOException while obtaining auth manager, + // throw RuntimeException so that the coprocessor is unloaded. + if (zk != null) { + try { + this.authManager = TableAuthManager.get(zk, env.getConfiguration()); + } catch (IOException ioe) { + throw new RuntimeException("Error obtaining TableAuthManager", ioe); + } + } else { + throw new RuntimeException("Error obtaining TableAuthManager, zk found null."); } } @@ -763,28 +778,36 @@ public class AccessController extends BaseRegionObserver /* ---- RegionObserver implementation ---- */ + @Override + public void preOpen(ObserverContext e) + throws IOException { + RegionCoprocessorEnvironment env = e.getEnvironment(); + final HRegion region = env.getRegion(); + if (region == null) { + LOG.error("NULL region from RegionCoprocessorEnvironment in preOpen()"); + return; + } else { + HRegionInfo regionInfo = region.getRegionInfo(); + if (isSpecialTable(regionInfo)) { + isSystemOrSuperUser(regionEnv.getConfiguration()); + } else { + requirePermission(Action.ADMIN); + } + } + } + @Override public void postOpen(ObserverContext c) { - RegionCoprocessorEnvironment e = c.getEnvironment(); - final HRegion region = e.getRegion(); + RegionCoprocessorEnvironment env = c.getEnvironment(); + final HRegion region = env.getRegion(); if (region == null) { LOG.error("NULL region from RegionCoprocessorEnvironment in postOpen()"); return; } - - try { - this.authManager = TableAuthManager.get( - e.getRegionServerServices().getZooKeeper(), - regionEnv.getConfiguration()); - } catch (IOException ioe) { - // pass along as a RuntimeException, so that the coprocessor is unloaded - throw new RuntimeException("Error obtaining TableAuthManager", ioe); - } - if (AccessControlLists.isAclRegion(region)) { aclRegion = true; try { - initialize(e); + initialize(env); } catch (IOException ex) { // if we can't obtain permissions, it's better to fail // than perform checks incorrectly @@ -1269,4 +1292,43 @@ public class AccessController extends BaseRegionObserver } return tableName; } + + + @Override + public void preClose(ObserverContext e, boolean abortRequested) + throws IOException { + requirePermission(Action.ADMIN); + } + + private void isSystemOrSuperUser(Configuration conf) throws IOException { + User user = User.getCurrent(); + if (user == null) { + throw new IOException("Unable to obtain the current user, " + + "authorization checks for internal operations will not work correctly!"); + } + + String currentUser = user.getShortName(); + List superusers = Lists.asList(currentUser, conf.getStrings( + AccessControlLists.SUPERUSER_CONF_KEY, new String[0])); + + User activeUser = getActiveUser(); + if (!(superusers.contains(activeUser.getShortName()))) { + throw new AccessDeniedException("User '" + (user != null ? user.getShortName() : "null") + + "is not system or super user."); + } + } + + private boolean isSpecialTable(HRegionInfo regionInfo) { + byte[] tableName = regionInfo.getTableName(); + return tableName.equals(AccessControlLists.ACL_TABLE_NAME) + || tableName.equals(Bytes.toBytes("-ROOT-")) + || tableName.equals(Bytes.toBytes(".META.")); + } + + @Override + public void preStopRegionServer( + ObserverContext env) + throws IOException { + requirePermission(Permission.Action.ADMIN); + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index d1affa744f9..c92e371e1e7 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.LargeTests; import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.UnknownRowLockException; import org.apache.hadoop.hbase.client.Append; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; @@ -49,6 +50,7 @@ import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; @@ -56,6 +58,7 @@ import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessCont import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost; +import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost; import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.Permission.Action; @@ -84,7 +87,7 @@ public class TestAccessController { private static User SUPERUSER; // user granted with all global permission private static User USER_ADMIN; - // user with rw permissions + // user with rw permissions on column family. private static User USER_RW; // user with read-only permissions private static User USER_RO; @@ -100,6 +103,7 @@ public class TestAccessController { private static MasterCoprocessorEnvironment CP_ENV; private static RegionCoprocessorEnvironment RCP_ENV; + private static RegionServerCoprocessorEnvironment RSCP_ENV; private static AccessController ACCESS_CONTROLLER; @BeforeClass @@ -114,6 +118,10 @@ public class TestAccessController { ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(AccessController.class.getName()); CP_ENV = cpHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); + RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0) + .getCoprocessorHost(); + RSCP_ENV = rsHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER, + Coprocessor.PRIORITY_HIGHEST, 1, conf); // Wait for the ACL table to become available TEST_UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME, 5000); @@ -1363,4 +1371,44 @@ public class TestAccessController { } } + + @Test + public void testStopRegionServer() throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public Object run() throws Exception { + ACCESS_CONTROLLER.preStopRegionServer(ObserverContext.createAndPrepare(RSCP_ENV, null)); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE); + } + + @Test + public void testOpenRegion() throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public Object run() throws Exception { + ACCESS_CONTROLLER.preOpen(ObserverContext.createAndPrepare(RCP_ENV, null)); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN); + verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); + } + + @Test + public void testCloseRegion() throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public Object run() throws Exception { + ACCESS_CONTROLLER.preClose(ObserverContext.createAndPrepare(RCP_ENV, null), false); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN); + verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); + } + }