From 7e8db1445ca1fbdd06e9b1a76facdb5d6d3e2e7d 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 --- .../hadoop/hbase/coprocessor/MasterObserver.java | 133 +++++++++++++++++++- .../org/apache/hadoop/hbase/master/HMaster.java | 29 ++++- .../hadoop/hbase/master/MasterCoprocessorHost.java | 137 ++++++++++++++++++++- .../hadoop/hbase/master/MasterRpcServices.java | 109 +++++++++++++--- .../hbase/security/access/AccessController.java | 42 +++++++ .../security/access/TestAccessController.java | 110 ++++++++++++++++- .../asciidoc/_chapters/appendix_acl_matrix.adoc | 10 ++ 7 files changed, 539 insertions(+), 31 deletions(-) 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 6ef5504ad9..0ff84dc902 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 @@ -34,6 +34,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; @@ -330,6 +331,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 @@ -532,7 +552,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 @@ -597,9 +616,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. @@ -610,6 +630,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 numCleaned number of cleaned catalogs + */ + default void postRunCatalogScan(final ObserverContext ctx, + int numCleaned) 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. @@ -780,8 +907,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 1c576209e8..a9493fced2 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 @@ -607,6 +607,7 @@ public class HMaster extends HRegionServer implements MasterServices { return connector.getLocalPort(); } + @Override protected Function getMetaTableObserver() { return builder -> builder.setRegionReplication(conf.getInt(HConstants.META_REPLICAS_NUM, HConstants.DEFAULT_META_REPLICA_NUM)); } @@ -1496,6 +1497,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<>( @@ -1526,9 +1541,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; @@ -1537,6 +1563,7 @@ public class HMaster extends HRegionServer implements MasterServices { /** * @return Client info for use as prefix on an audit log string; who did an action */ + @Override public String getClientIdAuditPrefix() { return "Client=" + RpcServer.getRequestUserName().orElse(null) + "/" + RpcServer.getRemoteAddress().orElse(null); @@ -3026,7 +3053,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 bc262290fc..3a06a4e215 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 @@ -50,13 +50,12 @@ import org.apache.hadoop.hbase.coprocessor.MasterObserver; import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor; import org.apache.hadoop.hbase.coprocessor.ObserverContext; 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; @@ -138,6 +137,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; } @@ -541,9 +541,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 { @@ -944,6 +960,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 numCleaned) throws IOException { + execOperation(coprocEnvironments.isEmpty() ? null : new MasterObserverOperation() { + @Override + public void call(MasterObserver observer) throws IOException { + observer.postRunCatalogScan(this, numCleaned); + } + }); + } + + 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 ce85b66cb4..e54d16e6b5 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 @@ -367,7 +367,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); } @@ -663,29 +671,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 @@ -811,18 +844,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; @@ -846,18 +888,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) { @@ -1395,7 +1445,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 numCleaned = master.catalogJanitorChore.scan(); + + if (master.cpHost != null) { + master.cpHost.postRunCatalogScan(numCleaned); + } + + return ResponseConverter.buildRunCatalogScanResponse(numCleaned); } catch (IOException ioe) { throw new ServiceException(ioe); } @@ -1406,8 +1467,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 0f9d8a5a8f..06b223477e 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 @@ -1217,6 +1217,12 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, Action.ADMIN, Action.CREATE); } + @Override + public void preExecProcedure(ObserverContext ctx, + String signature) throws IOException { + requirePermission(getActiveUser(ctx), "execProcedure", Action.ADMIN); + } + @Override public void preAbortProcedure(ObserverContext ctx, final long procId) throws IOException { @@ -1290,6 +1296,42 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, requirePermission(getActiveUser(c), "balanceSwitch", Action.ADMIN); } + @Override + public void preNormalize(ObserverContext c) + throws IOException { + requirePermission(getActiveUser(c), "normalize", Action.ADMIN); + } + + @Override + public void preNormalizeSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(getActiveUser(c), "normalizeSwitch", Action.ADMIN); + } + + @Override + public void preRunCatalogScan(ObserverContext c) + throws IOException { + requirePermission(getActiveUser(c), "runCatalogScan", Action.ADMIN); + } + + @Override + public void preCatalogJanitorSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(getActiveUser(c), "catalogJanitorSwitch", Action.ADMIN); + } + + @Override + public void preRunCleanerChore( + ObserverContext c) throws IOException { + requirePermission(getActiveUser(c), "runCleanerChore", Action.ADMIN); + } + + @Override + public void preCleanerChoreSwitch(ObserverContext c, + boolean on) throws IOException { + requirePermission(getActiveUser(c), "cleanerChoreSwitch", Action.ADMIN); + } + @Override public void preShutdown(ObserverContext c) 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 67914658df..d34f874615 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 @@ -549,10 +549,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); @@ -560,7 +575,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 @@ -735,6 +751,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 0c99b1f361..99914d2f47 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