From c583a2cd76622faa3c7657edc4187642a1235fdb Mon Sep 17 00:00:00 2001 From: Apekshit Sharma Date: Mon, 5 Feb 2018 14:40:38 -0800 Subject: [PATCH] HBASE-19400 Add missing security hooks for MasterService RPCs Main changes: - Added ADMIN permission check for following rpc calls: normalize, setNormalizerRunning, runCatalogScan, enableCatalogJanitor, runCleanerChore, setCleanerChoreRunning, execMasterService, execProcedure, execProcedureWithRet - Moved authorizationEnabled check to start of AccessChecker's functions. --- .../hadoop/hbase/coprocessor/MasterObserver.java | 4 + .../hadoop/hbase/master/MasterRpcServices.java | 123 +++++++++------------ .../hadoop/hbase/regionserver/HRegionServer.java | 2 +- .../hadoop/hbase/regionserver/RSRpcServices.java | 18 ++- .../hbase/security/access/AccessChecker.java | 90 ++++++++------- .../hbase/security/access/AccessController.java | 14 +-- 6 files changed, 124 insertions(+), 127 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 a17bc9f4f5..e76736f7fc 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 @@ -333,14 +333,18 @@ public interface MasterObserver { * Called before a abortProcedure request has been processed. * @param ctx the environment to interact with the framework and master * @param procId the Id of the procedure + * @deprecated Since 2.0. Will be removed in 3.0. */ + @Deprecated default void preAbortProcedure( ObserverContext ctx, final long procId) throws IOException {} /** * Called after a abortProcedure request has been processed. * @param ctx the environment to interact with the framework and master + * @deprecated Since 2.0. Will be removed in 3.0. */ + @Deprecated default void postAbortProcedure(ObserverContext ctx) throws IOException {} 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..837f7ff9b1 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 @@ -86,6 +86,7 @@ import org.apache.hadoop.hbase.replication.ReplicationPeerDescription; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.AccessChecker; import org.apache.hadoop.hbase.security.access.AccessController; +import org.apache.hadoop.hbase.security.access.Permission; import org.apache.hadoop.hbase.security.visibility.VisibilityController; import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; @@ -349,6 +350,24 @@ public class MasterRpcServices extends RSRpcServices return new MasterAnnotationReadingPriorityFunction(this); } + /** + * Checks for the following pre-checks in order: + *
    + *
  1. Master is initialized
  2. + *
  3. Rpc caller has admin permissions
  4. + *
+ * @param requestName name of rpc request. Used in reporting failures to provide context. + * @throws ServiceException If any of the above listed pre-check fails. + */ + private void rpcPreCheck(String requestName) throws ServiceException { + try { + master.checkInitialized(); + requirePermission(requestName, Permission.Action.ADMIN); + } catch (IOException ioe) { + throw new ServiceException(ioe); + } + } + enum BalanceSwitchMode { SYNC, ASYNC @@ -392,25 +411,6 @@ public class MasterRpcServices extends RSRpcServices return switchBalancer(b, BalanceSwitchMode.SYNC); } - /** - * Sets normalizer on/off flag in ZK. - */ - public boolean normalizerSwitch(boolean on) { - boolean oldValue = master.getRegionNormalizerTracker().isNormalizerOn(); - boolean newValue = on; - try { - try { - master.getRegionNormalizerTracker().setNormalizerOn(newValue); - } catch (KeeperException ke) { - throw new IOException(ke); - } - LOG.info(master.getClientIdAuditPrefix() + " set normalizerSwitch=" + newValue); - } catch (IOException ioe) { - LOG.warn("Error flipping normalizer switch", ioe); - } - return oldValue; - } - /** * @return list of blocking services and their security info classes that this server supports */ @@ -696,24 +696,16 @@ public class MasterRpcServices extends RSRpcServices @Override public EnableCatalogJanitorResponse enableCatalogJanitor(RpcController c, EnableCatalogJanitorRequest req) throws ServiceException { - try { - master.checkInitialized(); - } catch (IOException ioe) { - throw new ServiceException(ioe); - } + rpcPreCheck("enableCatalogJanitor"); return EnableCatalogJanitorResponse.newBuilder().setPrevValue( master.catalogJanitorChore.setEnabled(req.getEnable())).build(); } @Override - public SetCleanerChoreRunningResponse setCleanerChoreRunning(RpcController c, - SetCleanerChoreRunningRequest req) - throws ServiceException { - try { - master.checkInitialized(); - } catch (IOException ioe) { - throw new ServiceException(ioe); - } + public SetCleanerChoreRunningResponse setCleanerChoreRunning( + RpcController c, SetCleanerChoreRunningRequest req) throws ServiceException { + rpcPreCheck("setCleanerChoreRunning"); + boolean prevValue = master.getLogCleaner().getEnabled() && master.getHFileCleaner().getEnabled(); master.getLogCleaner().setEnabled(req.getOn()); @@ -793,10 +785,9 @@ public class MasterRpcServices extends RSRpcServices @Override public ClientProtos.CoprocessorServiceResponse execMasterService(final RpcController controller, final ClientProtos.CoprocessorServiceRequest request) throws ServiceException { + rpcPreCheck("execMasterService"); try { - master.checkInitialized(); ServerRpcController execController = new ServerRpcController(); - ClientProtos.CoprocessorServiceCall call = request.getCall(); String serviceName = call.getServiceName(); String methodName = call.getMethodName(); @@ -816,14 +807,11 @@ public class MasterRpcServices extends RSRpcServices final com.google.protobuf.Message.Builder responseBuilder = service.getResponsePrototype(methodDesc).newBuilderForType(); service.callMethod(methodDesc, execController, execRequest, - new com.google.protobuf.RpcCallback() { - @Override - public void run(com.google.protobuf.Message message) { + (message) -> { if (message != null) { responseBuilder.mergeFrom(message); } - } - }); + }); com.google.protobuf.Message execResult = responseBuilder.build(); if (execController.getFailedOn() != null) { throw execController.getFailedOn(); @@ -841,8 +829,8 @@ public class MasterRpcServices extends RSRpcServices @Override public ExecProcedureResponse execProcedure(RpcController controller, ExecProcedureRequest request) throws ServiceException { + rpcPreCheck("execProcedure"); try { - master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( desc.getSignature()); @@ -850,12 +838,8 @@ public class MasterRpcServices extends RSRpcServices throw new ServiceException(new DoNotRetryIOException("The procedure is not registered: " + desc.getSignature())); } - - LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " - + desc.getSignature()); - + LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " + desc.getSignature()); mpm.execProcedure(desc); - // send back the max amount of time the client should wait for the procedure // to complete long waitTime = SnapshotDescriptionUtils.DEFAULT_MAX_WAIT_TIME; @@ -876,21 +860,16 @@ public class MasterRpcServices extends RSRpcServices @Override public ExecProcedureResponse execProcedureWithRet(RpcController controller, ExecProcedureRequest request) throws ServiceException { + rpcPreCheck("execProcedureWithRet"); try { - master.checkInitialized(); ProcedureDescription desc = request.getProcedure(); - MasterProcedureManager mpm = master.getMasterProcedureManagerHost().getProcedureManager( - desc.getSignature()); + MasterProcedureManager mpm = + master.getMasterProcedureManagerHost().getProcedureManager(desc.getSignature()); if (mpm == null) { - throw new ServiceException("The procedure is not registered: " - + desc.getSignature()); + throw new ServiceException("The procedure is not registered: " + desc.getSignature()); } - - LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " - + desc.getSignature()); - + LOG.info(master.getClientIdAuditPrefix() + " procedure request for: " + desc.getSignature()); byte[] data = mpm.execProcedureWithRet(desc); - ExecProcedureResponse.Builder builder = ExecProcedureResponse.newBuilder(); // set return data if available if (data != null) { @@ -1189,8 +1168,8 @@ public class MasterRpcServices extends RSRpcServices @Override public AbortProcedureResponse abortProcedure( - RpcController rpcController, - AbortProcedureRequest request) throws ServiceException { + RpcController rpcController, AbortProcedureRequest request) throws ServiceException { + rpcPreCheck("abortProcedure"); try { AbortProcedureResponse.Builder response = AbortProcedureResponse.newBuilder(); boolean abortResult = @@ -1419,8 +1398,8 @@ public class MasterRpcServices extends RSRpcServices @Override public RunCatalogScanResponse runCatalogScan(RpcController c, RunCatalogScanRequest req) throws ServiceException { + rpcPreCheck("runCatalogScan"); try { - master.checkInitialized(); return ResponseConverter.buildRunCatalogScanResponse(master.catalogJanitorChore.scan()); } catch (IOException ioe) { throw new ServiceException(ioe); @@ -1430,13 +1409,9 @@ public class MasterRpcServices extends RSRpcServices @Override public RunCleanerChoreResponse runCleanerChore(RpcController c, RunCleanerChoreRequest req) throws ServiceException { - try { - master.checkInitialized(); - boolean result = master.getHFileCleaner().runCleaner() && master.getLogCleaner().runCleaner(); - return ResponseConverter.buildRunCleanerChoreResponse(result); - } catch (IOException ioe) { - throw new ServiceException(ioe); - } + rpcPreCheck("runCleanerChore"); + boolean result = master.getHFileCleaner().runCleaner() && master.getLogCleaner().runCleaner(); + return ResponseConverter.buildRunCleanerChoreResponse(result); } @Override @@ -1769,6 +1744,7 @@ public class MasterRpcServices extends RSRpcServices @Override public NormalizeResponse normalize(RpcController controller, NormalizeRequest request) throws ServiceException { + rpcPreCheck("normalize"); try { return NormalizeResponse.newBuilder().setNormalizerRan(master.normalizeRegions()).build(); } catch (IOException ex) { @@ -1779,13 +1755,18 @@ public class MasterRpcServices extends RSRpcServices @Override public SetNormalizerRunningResponse setNormalizerRunning(RpcController controller, SetNormalizerRunningRequest request) throws ServiceException { + rpcPreCheck("setNormalizerRunning"); + + // Sets normalizer on/off flag in ZK. + boolean prevValue = master.getRegionNormalizerTracker().isNormalizerOn(); + boolean newValue = request.getOn(); try { - master.checkInitialized(); - boolean prevValue = normalizerSwitch(request.getOn()); - return SetNormalizerRunningResponse.newBuilder().setPrevNormalizerValue(prevValue).build(); - } catch (IOException ioe) { - throw new ServiceException(ioe); + master.getRegionNormalizerTracker().setNormalizerOn(newValue); + } catch (KeeperException ke) { + LOG.warn("Error flipping normalizer switch", ke); } + LOG.info("{} set normalizerSwitch={}", master.getClientIdAuditPrefix(), newValue); + return SetNormalizerRunningResponse.newBuilder().setPrevNormalizerValue(prevValue).build(); } @Override @@ -2222,9 +2203,9 @@ public class MasterRpcServices extends RSRpcServices public ClearDeadServersResponse clearDeadServers(RpcController controller, ClearDeadServersRequest request) throws ServiceException { LOG.debug(master.getClientIdAuditPrefix() + " clear dead region servers."); + rpcPreCheck("clearDeadServers"); ClearDeadServersResponse.Builder response = ClearDeadServersResponse.newBuilder(); try { - master.checkInitialized(); if (master.cpHost != null) { master.cpHost.preClearDeadServers(); } 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 3a93c769fb..5025c84373 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 @@ -630,7 +630,7 @@ public class HRegionServer extends HasThread implements masterAddressTracker = null; clusterStatusTracker = null; } - this.rpcServices.start(); + this.rpcServices.start(zooKeeper); // This violates 'no starting stuff in Constructor' but Master depends on the below chore // and executor being created and takes a different startup route. Lots of overlap between HRS // and M (An M IS A HRS now). Need to refactor so less duplication between M and its super diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java index 44934a6e82..c455888715 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java @@ -118,6 +118,8 @@ import org.apache.hadoop.hbase.regionserver.handler.OpenPriorityRegionHandler; import org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler; import org.apache.hadoop.hbase.security.Superusers; import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessChecker; +import org.apache.hadoop.hbase.security.access.Permission; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.DNS; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; @@ -128,6 +130,7 @@ import org.apache.hadoop.hbase.wal.WAL; import org.apache.hadoop.hbase.wal.WALEdit; import org.apache.hadoop.hbase.wal.WALKey; import org.apache.hadoop.hbase.wal.WALSplitter; +import org.apache.hadoop.hbase.zookeeper.ZKWatcher; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -321,6 +324,13 @@ public class RSRpcServices implements HBaseRPCErrorHandler, final AtomicBoolean clearCompactionQueues = new AtomicBoolean(false); + // We want to vet all accesses at the point of entry itself; limiting scope of master's access + // checker instance to only this rpc services class to prevent its use spreading deeper into + // implementation. + // Initialized during start() since it needs ZKWatcher which is created by HRegionServer after + // RSRpcServices. + private AccessChecker accessChecker; + /** * Services launched in RSRpcServices. By default they are on but you can use the below * booleans to selectively enable/disable either Admin or Client Service (Rare is the case @@ -1236,6 +1246,11 @@ public class RSRpcServices implements HBaseRPCErrorHandler, return new AnnotationReadingPriorityFunction(this); } + protected void requirePermission(String request, Permission.Action perm) throws IOException { + accessChecker.requirePermission(RpcServer.getRequestUser().orElse(null), request, perm); + } + + public static String getHostname(Configuration conf, boolean isMaster) throws UnknownHostException { String hostname = conf.get(isMaster? HRegionServer.MASTER_HOSTNAME_KEY : @@ -1400,7 +1415,8 @@ public class RSRpcServices implements HBaseRPCErrorHandler, return regionServer.getRegionServerSpaceQuotaManager(); } - void start() { + void start(ZKWatcher zkWatcher) { + accessChecker = new AccessChecker(getConfiguration(), zkWatcher); this.scannerIdGenerator = new ScannerIdGenerator(this.regionServer.serverName); rpcServer.start(); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java index d88e5229af..1210e8c36f 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java @@ -87,6 +87,9 @@ public final class AccessChecker { */ public void requireAccess(User user, String request, TableName tableName, Action... permissions) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result = null; for (Action permission : permissions) { @@ -101,7 +104,7 @@ public final class AccessChecker { } } logResult(result); - if (authorizationEnabled && !result.isAllowed()) { + if (!result.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); } } @@ -130,22 +133,21 @@ public final class AccessChecker { public void requireGlobalPermission(User user, String request, Action perm, TableName tableName, Map> familyMap)throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result; if (authManager.authorize(user, perm)) { - result = AuthResult.allow(request, "Global check allowed", - user, perm, tableName, familyMap); + result = AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap); result.getParams().setTableName(tableName).setFamilies(familyMap); logResult(result); } else { - result = AuthResult.deny(request, "Global check failed", - user, perm, tableName, familyMap); + result = AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap); result.getParams().setTableName(tableName).setFamilies(familyMap); logResult(result); - if (authorizationEnabled) { - throw new AccessDeniedException( - "Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") - + "' (global, action=" + perm.toString() + ")"); - } + throw new AccessDeniedException( + "Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") + + "' (global, action=" + perm.toString() + ")"); } } @@ -159,22 +161,21 @@ public final class AccessChecker { */ public void requireGlobalPermission(User user, String request, Action perm, String namespace) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult authResult; if (authManager.authorize(user, perm)) { - authResult = AuthResult.allow(request, "Global check allowed", - user, perm, null); + authResult = AuthResult.allow(request, "Global check allowed", user, perm, null); authResult.getParams().setNamespace(namespace); logResult(authResult); } else { - authResult = AuthResult.deny(request, "Global check failed", - user, perm, null); + authResult = AuthResult.deny(request, "Global check failed", user, perm, null); authResult.getParams().setNamespace(namespace); logResult(authResult); - if (authorizationEnabled) { - throw new AccessDeniedException( - "Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") - + "' (global, action=" + perm.toString() + ")"); - } + throw new AccessDeniedException( + "Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") + + "' (global, action=" + perm.toString() + ")"); } } @@ -186,22 +187,23 @@ public final class AccessChecker { */ public void requireNamespacePermission(User user, String request, String namespace, Action... permissions) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result = null; for (Action permission : permissions) { if (authManager.authorize(user, namespace, permission)) { result = - AuthResult.allow(request, "Namespace permission granted", - user, permission, namespace); + AuthResult.allow(request, "Namespace permission granted", user, permission, namespace); break; } else { // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", - user, permission, namespace); + result = AuthResult.deny(request, "Insufficient permissions", user, permission, namespace); } } logResult(result); - if (authorizationEnabled && !result.isAllowed()) { + if (!result.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); } } @@ -215,24 +217,25 @@ public final class AccessChecker { public void requireNamespacePermission(User user, String request, String namespace, TableName tableName, Map> familyMap, Action... permissions) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result = null; for (Action permission : permissions) { if (authManager.authorize(user, namespace, permission)) { result = - AuthResult.allow(request, "Namespace permission granted", - user, permission, namespace); + AuthResult.allow(request, "Namespace permission granted", user, permission, namespace); result.getParams().setTableName(tableName).setFamilies(familyMap); break; } else { // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", - user, permission, namespace); + result = AuthResult.deny(request, "Insufficient permissions", user, permission, namespace); result.getParams().setTableName(tableName).setFamilies(familyMap); } } logResult(result); - if (authorizationEnabled && !result.isAllowed()) { + if (!result.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); } } @@ -249,23 +252,24 @@ public final class AccessChecker { */ public void requirePermission(User user, String request, TableName tableName, byte[] family, byte[] qualifier, Action... permissions) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result = null; for (Action permission : permissions) { if (authManager.authorize(user, tableName, family, qualifier, permission)) { result = AuthResult.allow(request, "Table permission granted", - user, permission, tableName, family, - qualifier); + user, permission, tableName, family, qualifier); break; } else { // rest of the world result = AuthResult.deny(request, "Insufficient permissions", - user, permission, tableName, family, - qualifier); + user, permission, tableName, family, qualifier); } } logResult(result); - if (authorizationEnabled && !result.isAllowed()) { + if (!result.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); } } @@ -283,6 +287,9 @@ public final class AccessChecker { public void requireTablePermission(User user, String request, TableName tableName,byte[] family, byte[] qualifier, Action... permissions) throws IOException { + if (!authorizationEnabled) { + return; + } AuthResult result = null; for (Action permission : permissions) { @@ -299,7 +306,7 @@ public final class AccessChecker { } } logResult(result); - if (authorizationEnabled && !result.isAllowed()) { + if (!result.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); } } @@ -321,12 +328,13 @@ public final class AccessChecker { public static void logResult(AuthResult result) { if (AUDITLOG.isTraceEnabled()) { - AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + " for user " + ( - result.getUser() != null ? - result.getUser().getShortName() : - "UNKNOWN") + "; reason: " + result.getReason() + "; remote address: " - + RpcServer.getRemoteAddress().map(InetAddress::toString).orElse("") - + "; request: " + result.getRequest() + "; context: " + result.toContextString()); + AUDITLOG.trace( + "Access {} for user {}; reason: {}; remote address: {}; request: {}; context: {}", + (result.isAllowed() ? "allowed" : "denied"), + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN"), + result.getReason(), + RpcServer.getRemoteAddress().map(InetAddress::toString).orElse(""), + result.getRequest(), result.toContextString()); } } } \ 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 1fbf01d459..a65f2b1410 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 @@ -1022,12 +1022,6 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, requirePermission(ctx, "abortProcedure", Action.ADMIN); } - @Override - public void postAbortProcedure(ObserverContext ctx) - throws IOException { - // There is nothing to do at this time after the procedure abort request was sent. - } - @Override public void preGetProcedures(ObserverContext ctx) throws IOException { @@ -1278,12 +1272,6 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, null, null, Action.ADMIN); } - @Override - public void preClearDeadServers(ObserverContext ctx) - throws IOException { - requirePermission(ctx, "clearDeadServers", Action.ADMIN); - } - @Override public void preDecommissionRegionServers(ObserverContext ctx, List servers, boolean offload) throws IOException { @@ -2456,7 +2444,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, throws IOException { requirePermission(ctx, "replicateLogEntries", Action.WRITE); } - + @Override public void preClearCompactionQueues(ObserverContext ctx) throws IOException { -- 2.14.1