From 51576bf6d9b69993346ceea692588a34c28c45fe Mon Sep 17 00:00:00 2001 From: Balazs Meszaros Date: Thu, 14 Dec 2017 16:47:36 +0100 Subject: [PATCH] HBASE-19400 Add missing security hooks for MasterService RPCs --- .../hbase/shaded/protobuf/ResponseConverter.java | 4 +- .../hadoop/hbase/coprocessor/MasterObserver.java | 133 +++++++++++++++++++- .../org/apache/hadoop/hbase/master/HMaster.java | 27 +++- .../hadoop/hbase/master/MasterCoprocessorHost.java | 137 ++++++++++++++++++++- .../hadoop/hbase/master/MasterRpcServices.java | 109 +++++++++++++--- .../hbase/security/access/AccessController.java | 44 ++++++- .../security/access/TestAccessController.java | 110 ++++++++++++++++- .../asciidoc/_chapters/appendix_acl_matrix.adoc | 10 ++ 8 files changed, 540 insertions(+), 34 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ResponseConverter.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ResponseConverter.java index 255d9f5d4f..44db710d92 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ResponseConverter.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ResponseConverter.java @@ -292,8 +292,8 @@ public final class ResponseConverter { * Creates a response for the catalog scan request * @return A RunCatalogScanResponse */ - public static RunCatalogScanResponse buildRunCatalogScanResponse(int numCleaned) { - return RunCatalogScanResponse.newBuilder().setScanResult(numCleaned).build(); + public static RunCatalogScanResponse buildRunCatalogScanResponse(int jobsStarted) { + return RunCatalogScanResponse.newBuilder().setScanResult(jobsStarted).build(); } /** diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java index a17bc9f4f5..bcbd93c563 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java @@ -33,6 +33,7 @@ import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.SnapshotDescription; import org.apache.hadoop.hbase.client.TableDescriptor; import org.apache.hadoop.hbase.master.RegionPlan; +import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan; import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.quotas.GlobalQuotaSettings; import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; @@ -329,6 +330,25 @@ public interface MasterObserver { final ObserverContext ctx, final TableName tableName) throws IOException {} + /** + * Called before an execProcedure request has been processed. + * @param ctx the environment to interact with the framework and master + * @param signature signature of the procedure + */ + default void preExecProcedure(final ObserverContext ctx, + String signature) throws IOException { + } + + /** + * Called after an execProcedure request has been processed. + * @param ctx the environment to interact with the framework and master + * @param signature signature of the procedure + * @param result result of the procedure + */ + default void postExecProcedure(final ObserverContext ctx, + String signature, byte[] result) throws IOException { + } + /** * Called before a abortProcedure request has been processed. * @param ctx the environment to interact with the framework and master @@ -531,7 +551,6 @@ public interface MasterObserver { final byte[] splitKey, final List metaEntries) throws IOException {} - /** * This will be called after update META step as part of split transaction * @param ctx the environment to interact with the framework and master @@ -596,9 +615,10 @@ public interface MasterObserver { /** * Called prior to modifying the flag used to enable/disable region balancing. * @param ctx the coprocessor instance's environment + * @param on the new balanceSwitch value */ default void preBalanceSwitch(final ObserverContext ctx, - final boolean newValue) throws IOException {} + final boolean on) throws IOException {} /** * Called after the flag to enable/disable balancing has changed. @@ -609,6 +629,113 @@ public interface MasterObserver { default void postBalanceSwitch(final ObserverContext ctx, final boolean oldValue, final boolean newValue) throws IOException {} + /** + * Called prior to normalizing regions. + * @param ctx the environment to interact with the framework and master + */ + default void preNormalize(final ObserverContext ctx) + throws IOException { + } + + /** + * Called after the regions have been normalized. + * @param ctx the environment to interact with the framework and master + */ + default void postNormalize(final ObserverContext ctx, + List plans) throws IOException { + } + + /** + * Called prior to modifying the flag used to enable/disable region normalizer. + * @param ctx the coprocessor instance's environment + * @param on the new normalizeSwitch value + */ + default void preNormalizeSwitch(final ObserverContext ctx, + final boolean on) throws IOException { + } + + /** + * Called after the flag to enable/disable normalizer has changed. + * @param ctx the coprocessor instance's environment + * @param oldValue the previously set normalizeSwitch value + * @param newValue the newly set normalizeSwitch value + */ + default void postNormalizeSwitch(final ObserverContext ctx, + final boolean oldValue, final boolean newValue) throws IOException { + } + + /** + * Called prior to run catalog scan. + * @param ctx the environment to interact with the framework and master + */ + default void preRunCatalogScan(final ObserverContext ctx) + throws IOException { + } + + /** + * Called after catalog scan has run. + * @param ctx the environment to interact with the framework and master + * @param jobsStarted number of archiving jobs started. + */ + default void postRunCatalogScan(final ObserverContext ctx, + int jobsStarted) throws IOException { + } + + /** + * Called prior to modifying the flag used to enable/disable catalog janitor. + * @param ctx the coprocessor instance's environment + * @param on the new catalogJanitorSwitch value + */ + default void preCatalogJanitorSwitch(final ObserverContext ctx, + final boolean on) throws IOException { + } + + /** + * Called after the flag to enable/disable catalog janitor has changed. + * @param ctx the coprocessor instance's environment + * @param oldValue the previously set balanceSwitch value + * @param newValue the newly set balanceSwitch value + */ + default void postCatalogJanitorSwitch(final ObserverContext ctx, + final boolean oldValue, final boolean newValue) throws IOException { + } + + /** + * Called prior to run cleaner chore. + * @param ctx the environment to interact with the framework and master + */ + default void preRunCleanerChore(final ObserverContext ctx) + throws IOException { + } + + /** + * Called after cleaner chore has run. + * @param ctx the environment to interact with the framework and master + * @param ran true if it ran, false otherwise + */ + default void postRunCleanerChore(final ObserverContext ctx, + boolean ran) throws IOException { + } + + /** + * Called prior to modifying the flag used to enable/disable cleaner chore. + * @param ctx the coprocessor instance's environment + * @param on the new cleanerChoreSwitch value + */ + default void preCleanerChoreSwitch(final ObserverContext ctx, + final boolean on) throws IOException { + } + + /** + * Called after the flag to enable/disable cleaner chore has changed. + * @param ctx the coprocessor instance's environment + * @param oldValue the previously set cleanerChoreSwitch value + * @param newValue the newly set cleanerChoreSwitch value + */ + default void postCleanerChoreSwitch(final ObserverContext ctx, + final boolean oldValue, final boolean newValue) throws IOException { + } + /** * Called prior to shutting down the full HBase cluster, including this * {@link org.apache.hadoop.hbase.master.HMaster} process. @@ -779,8 +906,6 @@ public interface MasterObserver { default void postGetTableNames(ObserverContext ctx, List descriptors, String regex) throws IOException {} - - /** * Called before a new namespace is created by * {@link org.apache.hadoop.hbase.master.HMaster}. diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 0e626ce6ff..e24ce8ed5b 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -1505,6 +1505,20 @@ public class HMaster extends HRegionServer implements MasterServices { return false; } + if (cpHost != null) { + try { + if (this.cpHost.preNormalize()) { + LOG.debug("Coprocessor bypassing normalizer request"); + return false; + } + } catch (IOException e) { + LOG.error("Error invoking master coprocessor preNormalize()", e); + return false; + } + } + + List allPlans = new ArrayList<>(); + synchronized (this.normalizer) { // Don't run the normalizer concurrently List allEnabledTables = new ArrayList<>( @@ -1535,9 +1549,20 @@ public class HMaster extends HRegionServer implements MasterServices { mergePlanCount++; } } + allPlans.addAll(plans); } } } + + if (cpHost != null) { + try { + cpHost.postNormalize(allPlans); + } catch (IOException e) { + // normalizing already succeeded so don't change the result + LOG.error("Error invoking master coprocessor postNormalize()", e); + } + } + // If Region did not generate any plans, it means the cluster is already balanced. // Return true indicating a success. return true; @@ -3029,7 +3054,7 @@ public class HMaster extends HRegionServer implements MasterServices { public boolean abortProcedure(final long procId, final boolean mayInterruptIfRunning) throws IOException { if (cpHost != null) { - cpHost.preAbortProcedure(this.procedureExecutor, procId); + cpHost.preAbortProcedure(procId); } final boolean result = this.procedureExecutor.abort(procId, mayInterruptIfRunning); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java index 10e1d0a539..a67634ef63 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java @@ -46,13 +46,12 @@ import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.MasterObserver; import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor; import org.apache.hadoop.hbase.master.locking.LockProcedure; -import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; +import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan; import org.apache.hadoop.hbase.metrics.MetricRegistry; import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.procedure2.LockType; import org.apache.hadoop.hbase.procedure2.LockedResource; import org.apache.hadoop.hbase.procedure2.Procedure; -import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.quotas.GlobalQuotaSettings; import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; import org.apache.hadoop.hbase.security.User; @@ -133,6 +132,7 @@ public class MasterCoprocessorHost * @return An instance of MasterServices, an object NOT for general user-space Coprocessor * consumption. */ + @Override public MasterServices getMasterServices() { return this.masterServices; } @@ -536,9 +536,25 @@ public class MasterCoprocessorHost }); } - public void preAbortProcedure( - final ProcedureExecutor procEnv, - final long procId) throws IOException { + public void preExecProcedure(String signature) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preExecProcedure(this, signature); + } + }); + } + + public void postExecProcedure(String signature, byte[] result) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postExecProcedure(this, signature, result); + } + }); + } + + public void preAbortProcedure(final long procId) throws IOException { execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { @Override public void call(MasterObserver observer) throws IOException { @@ -939,6 +955,117 @@ public class MasterCoprocessorHost }); } + public boolean preNormalize() throws IOException { + return execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preNormalize(this); + } + }); + } + + public void postNormalize(final List plans) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postNormalize(this, plans); + } + }); + } + + public void preNormalizeSwitch(final boolean on) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preNormalizeSwitch(this, on); + } + }); + } + + public void postNormalizeSwitch(final boolean oldValue, final boolean newValue) + throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postNormalizeSwitch(this, oldValue, newValue); + } + }); + } + + public boolean preRunCatalogScan() throws IOException { + return execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preRunCatalogScan(this); + } + }); + } + + public void postRunCatalogScan(final int jobsStarted) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postRunCatalogScan(this, jobsStarted); + } + }); + } + + public void preCatalogJanitorSwitch(final boolean on) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preCatalogJanitorSwitch(this, on); + } + }); + } + + public void postCatalogJanitorSwitch(final boolean oldValue, final boolean newValue) + throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postCatalogJanitorSwitch(this, oldValue, newValue); + } + }); + } + + public boolean preRunCleanerChore() throws IOException { + return execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preRunCleanerChore(this); + } + }); + } + + public void postRunCleanerChore(final boolean ran) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postRunCleanerChore(this, ran); + } + }); + } + + public void preCleanerChoreSwitch(final boolean on) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.preCleanerChoreSwitch(this, on); + } + }); + } + + public void postCleanerChoreSwitch(final boolean oldValue, final boolean newValue) + throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postCleanerChoreSwitch(this, oldValue, newValue); + } + }); + } + public void preShutdown() throws IOException { // While stopping the cluster all coprocessors method should be executed first then the // coprocessor should be cleaned up. diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java index 377a9c6dec..440d77981c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java @@ -400,7 +400,15 @@ public class MasterRpcServices extends RSRpcServices boolean newValue = on; try { try { + if (master.cpHost != null) { + master.cpHost.preNormalizeSwitch(newValue); + } + master.getRegionNormalizerTracker().setNormalizerOn(newValue); + + if (master.cpHost != null) { + master.cpHost.postNormalizeSwitch(oldValue, newValue); + } } catch (KeeperException ke) { throw new IOException(ke); } @@ -696,29 +704,54 @@ public class MasterRpcServices extends RSRpcServices @Override public EnableCatalogJanitorResponse enableCatalogJanitor(RpcController c, EnableCatalogJanitorRequest req) throws ServiceException { + boolean newValue = req.getEnable(); + boolean oldValue; + try { master.checkInitialized(); + + if (master.cpHost != null) { + master.cpHost.preCatalogJanitorSwitch(newValue); + } + + oldValue = master.catalogJanitorChore.setEnabled(newValue); + + if (master.cpHost != null) { + master.cpHost.postCatalogJanitorSwitch(oldValue, newValue); + } } catch (IOException ioe) { throw new ServiceException(ioe); } - return EnableCatalogJanitorResponse.newBuilder().setPrevValue( - master.catalogJanitorChore.setEnabled(req.getEnable())).build(); + + return EnableCatalogJanitorResponse.newBuilder().setPrevValue(oldValue).build(); } @Override public SetCleanerChoreRunningResponse setCleanerChoreRunning(RpcController c, SetCleanerChoreRunningRequest req) throws ServiceException { + boolean oldValue = master.getLogCleaner().getEnabled() && + master.getHFileCleaner().getEnabled(); + boolean newValue = req.getOn(); + try { master.checkInitialized(); + + if (master.cpHost != null) { + master.cpHost.preCleanerChoreSwitch(newValue); + } + + master.getLogCleaner().setEnabled(newValue); + master.getHFileCleaner().setEnabled(newValue); + + if (master.cpHost != null) { + master.cpHost.postCleanerChoreSwitch(oldValue, newValue); + } } catch (IOException ioe) { throw new ServiceException(ioe); } - boolean prevValue = - master.getLogCleaner().getEnabled() && master.getHFileCleaner().getEnabled(); - master.getLogCleaner().setEnabled(req.getOn()); - master.getHFileCleaner().setEnabled(req.getOn()); - return SetCleanerChoreRunningResponse.newBuilder().setPrevValue(prevValue).build(); + + return SetCleanerChoreRunningResponse.newBuilder().setPrevValue(oldValue).build(); } @Override @@ -844,18 +877,27 @@ public class MasterRpcServices extends RSRpcServices try { master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); + String signature = desc.getSignature(); + + if (master.cpHost != null) { + master.cpHost.preExecProcedure(signature); + } + MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( - desc.getSignature()); + signature); if (mpm == null) { throw new ServiceException(new DoNotRetryIOException("The procedure is not registered: " - + desc.getSignature())); + + signature)); } - LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " - + desc.getSignature()); + LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " + signature); mpm.execProcedure(desc); + if (master.cpHost != null) { + master.cpHost.postExecProcedure(signature, null); + } + // send back the max amount of time the client should wait for the procedure // to complete long waitTime = SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME; @@ -879,18 +921,26 @@ public class MasterRpcServices extends RSRpcServices try { master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); + String signature = desc.getSignature(); + + if (master.cpHost != null) { + master.cpHost.preExecProcedure(signature); + } + MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( - desc.getSignature()); + signature); if (mpm == null) { - throw new ServiceException("The procedure is not registered: " - + desc.getSignature()); + throw new ServiceException("The procedure is not registered: " + signature); } - LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " - + desc.getSignature()); + LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " + signature); byte[] data = mpm.execProcedureWithRet(desc); + if (master.cpHost != null) { + master.cpHost.preExecProcedure(signature); + } + ExecProcedureResponse.Builder builder = ExecProcedureResponse.newBuilder(); // set return data if available if (data != null) { @@ -1421,7 +1471,18 @@ public class MasterRpcServices extends RSRpcServices RunCatalogScanRequest req) throws ServiceException { try { master.checkInitialized(); - return ResponseConverter.buildRunCatalogScanResponse(master.catalogJanitorChore.scan()); + + if (master.cpHost != null) { + master.cpHost.preRunCatalogScan(); + } + + int jobsStarted = master.catalogJanitorChore.scan(); + + if (master.cpHost != null) { + master.cpHost.postRunCatalogScan(jobsStarted); + } + + return ResponseConverter.buildRunCatalogScanResponse(jobsStarted); } catch (IOException ioe) { throw new ServiceException(ioe); } @@ -1432,8 +1493,18 @@ public class MasterRpcServices extends RSRpcServices throws ServiceException { try { master.checkInitialized(); - boolean result = master.getHFileCleaner().runCleaner() && master.getLogCleaner().runCleaner(); - return ResponseConverter.buildRunCleanerChoreResponse(result); + + if (master.cpHost != null) { + master.cpHost.preRunCleanerChore(); + } + + boolean ran = master.getHFileCleaner().runCleaner() && master.getLogCleaner().runCleaner(); + + if (master.cpHost != null) { + master.cpHost.postRunCleanerChore(ran); + } + + return ResponseConverter.buildRunCleanerChoreResponse(ran); } catch (IOException ioe) { throw new ServiceException(ioe); } 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 60842be57b..c4bccc301e 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 @@ -1016,6 +1016,12 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, tableName, null, null, Action.ADMIN, Action.CREATE); } + @Override + public void preExecProcedure(ObserverContext ctx, + String signature) throws IOException { + requirePermission(ctx, "execProcedure", Action.ADMIN); + } + @Override public void preAbortProcedure(ObserverContext ctx, final long procId) throws IOException { @@ -1088,6 +1094,42 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, requirePermission(c, "balanceSwitch", Action.ADMIN); } + @Override + public void preNormalize(ObserverContext c) + throws IOException { + requirePermission(c, "normalize", Action.ADMIN); + } + + @Override + public void preNormalizeSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(c, "normalizeSwitch", Action.ADMIN); + } + + @Override + public void preRunCatalogScan(ObserverContext c) + throws IOException { + requirePermission(c, "runCatalogScan", Action.ADMIN); + } + + @Override + public void preCatalogJanitorSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(c, "catalogJanitorSwitch", Action.ADMIN); + } + + @Override + public void preRunCleanerChore( + ObserverContext c) throws IOException { + requirePermission(c, "runCleanerChore", Action.ADMIN); + } + + @Override + public void preCleanerChoreSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(c, "cleanerChoreSwitch", Action.ADMIN); + } + @Override public void preShutdown(ObserverContext c) throws IOException { @@ -2456,7 +2498,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, throws IOException { requirePermission(ctx, "replicateLogEntries", Action.WRITE); } - + @Override public void preClearCompactionQueues(ObserverContext ctx) throws IOException { 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 af55dca056..02d08050d4 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 @@ -542,10 +542,25 @@ public class TestAccessController extends SecureTestUtil { } } + @Test + public void testExecProcedure() throws Exception { + String signature = "signature"; + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preExecProcedure(ObserverContextImpl.createAndPrepare(CP_ENV), signature); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); + } + @Test public void testAbortProcedure() throws Exception { long procId = 1; - AccessTestAction abortProcedureAction = new AccessTestAction() { + AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preAbortProcedure(ObserverContextImpl.createAndPrepare(CP_ENV), procId); @@ -553,7 +568,8 @@ public class TestAccessController extends SecureTestUtil { } }; - verifyAllowed(abortProcedureAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test @@ -728,6 +744,96 @@ public class TestAccessController extends SecureTestUtil { USER_GROUP_WRITE, USER_GROUP_CREATE); } + @Test (timeout=180000) + public void testNormalize() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preNormalize(ObserverContextImpl.createAndPrepare(CP_ENV)); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + + @Test (timeout=180000) + public void testNormalizeSwitch() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preNormalizeSwitch(ObserverContextImpl.createAndPrepare(CP_ENV), true); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + + @Test (timeout=180000) + public void testRunCatalogScan() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preRunCatalogScan(ObserverContextImpl.createAndPrepare(CP_ENV)); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + + @Test (timeout=180000) + public void testCatalogJanitorSwitch() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preCatalogJanitorSwitch(ObserverContextImpl.createAndPrepare(CP_ENV), true); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + + @Test (timeout=180000) + public void testRunCleanerChore() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preRunCleanerChore(ObserverContextImpl.createAndPrepare(CP_ENV)); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + + @Test (timeout=180000) + public void testCleanerChoreSwitch() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preCleanerChoreSwitch(ObserverContextImpl.createAndPrepare(CP_ENV), true); + return null; + } + }; + + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); + verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, + USER_GROUP_WRITE, USER_GROUP_CREATE); + } + @Test (timeout=180000) public void testShutdown() throws Exception { AccessTestAction action = new AccessTestAction() { diff --git a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc index d5ea0765ba..db0d69e7eb 100644 --- a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc +++ b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc @@ -90,12 +90,22 @@ In case the table goes out of date, the unit tests which check for accuracy of p | | enableTable | superuser\|global(A)\|global\(C)\|NS(A)\|NS\(C)\|TableOwner\|table(A)\|table\(C) | | disableTable | superuser\|global(A)\|global\(C)\|NS(A)\|NS\(C)\|TableOwner\|table(A)\|table\(C) | | disableAclTable | Not allowed +| | execProcedure | superuser\|global(A) +| | abortProcedure | superuser\|global(A) +| | getProcedure | superuser\|global(A) +| | getLocks | superuser\|global(A) | | move | superuser\|global(A)\|NS(A)\|TableOwner\|table(A) | | assign | superuser\|global(A)\|NS(A)\|TableOwner\|table(A) | | unassign | superuser\|global(A)\|NS(A)\|TableOwner\|table(A) | | regionOffline | superuser\|global(A)\|NS(A)\|TableOwner\|table(A) | | balance | superuser\|global(A) | | balanceSwitch | superuser\|global(A) +| | normalize | superuser\|global(A) +| | normalizeSwitch | superuser\|global(A) +| | runCatalogScan | superuser\|global(A) +| | catalogJanitorSwitch | superuser\|global(A) +| | runCleanerChore | superuser\|global(A) +| | cleanerChoreSwitch | superuser\|global(A) | | shutdown | superuser\|global(A) | | stopMaster | superuser\|global(A) | | snapshot | superuser\|global(A)\|NS(A)\|TableOwner\|table(A) -- 2.15.1