From 1ef06216950dcf9a3d5e60c4bef7931d154815e7 Mon Sep 17 00:00:00 2001
From: Andrew Kyle Purtell
Date: Fri, 14 Dec 2012 21:12:36 +0000
Subject: [PATCH] HBASE-7331. Add access control for region open and close, and
stopping the regionserver (Vandana Ayyalasomayajula)
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1422094 13f79535-47bb-0310-9956-ffa450edef68
---
.../hbase/coprocessor/BaseRegionObserver.java | 6 +-
.../hbase/coprocessor/CoprocessorHost.java | 2 +
.../hbase/coprocessor/RegionObserver.java | 6 +-
.../RegionServerCoprocessorEnvironment.java | 31 +++++
.../coprocessor/RegionServerObserver.java | 35 ++++++
.../hbase/regionserver/HRegionServer.java | 41 ++++++-
.../regionserver/RegionCoprocessorHost.java | 23 ++--
.../RegionServerCoprocessorHost.java | 108 ++++++++++++++++++
.../security/access/AccessController.java | 106 +++++++++++++----
.../security/access/TestAccessController.java | 50 +++++++-
10 files changed, 364 insertions(+), 44 deletions(-)
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerObserver.java
create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java
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);
+ }
+
}