Add a Maintain Server global capability

This capability allows for general maintenance tasks like flushing
caches and reindexing changes, without requiring either Administrate
Server (which is too broad) or a handful of other minor capabilities
(which we don't want to grow out of control).

Change-Id: I3dd75d14e7b0303b23bad24dffb9bfdfb515efc8
This commit is contained in:
Dave Borowitz
2015-06-11 15:35:48 -04:00
parent 4b61a78478
commit 664d040c79
25 changed files with 166 additions and 79 deletions

View File

@@ -1244,6 +1244,22 @@ kill command ends tasks that currently occupy the Gerrit server, usually
a replication task or a user initiated task such as an upload-pack or
receive-pack.
[[capability_maintainServer]]
=== Maintain Server
Allow basic, constrained server maintenance tasks, such as flushing caches and
reindexing changes. Does not grant arbitrary database access, read/write, or
ACL management permissions, as might the
<<capability_administrateServer,administrate server capability>>.
Implies the following capabilities:
* <<capability_flushCaches,Flush Caches>>
* <<capability_kill,Kill Task>>
* <<capability_runGC,Run Garbage Collection>>
* <<capability_viewCaches,View Caches>>
* <<capability_viewQueue,View Queue>>
[[capability_modifyAccount]]
=== Modify Account

View File

@@ -21,9 +21,16 @@ the Gerrit web interface.
If no options are supplied, defaults to `--all`.
== ACCESS
Caller must be a member of the privileged 'Administrators' group,
or in a group that have been granted
link:access-control.html#capability_flushCaches[the 'Flush Caches' global capability].
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_flushCaches[Flush Caches] (any cache
except "web_sessions")
* link:access-control.html#capability_maintainServer[Maintain Server] (any cache
including "web_sessions")
* link:access-control.html#capability_administrateServer[Administrate Server]
(any cache including "web_sessions")
== SCRIPTING
This command is intended to be used in scripts.

View File

@@ -30,14 +30,17 @@ Display statistics about the size and hit ratio of in-memory caches.
Width of the output table.
== ACCESS
The caller must be a member of a group that is granted the
link:access-control.html#capability_viewCaches[View Caches] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_viewCaches[View Caches]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
The summary information about SSH, threads, tasks, memory and JVM are
only printed out if the caller is a member of a group that is granted
the link:access-control.html#capability_administrateServer[Administrate
Server] or link:access-control.html#capability_maintainServer[Maintain
Server] capability.
== SCRIPTING

View File

@@ -1324,6 +1324,9 @@ link:access-control.html#capability_flushCaches[Flush Caches]
capability.
|`killTask` |not set if `false`|Whether the user has the
link:access-control.html#capability_kill[Kill Task] capability.
|`maintainServer` |not set if `false`|Whether the user has the
link:access-control.html#capability_maintainServer[Maintain
Server] capability.
|`priority` |not set if `INTERACTIVE`|The name of the thread
pool used by the user, see link:access-control.html#capability_priority[
Priority] capability.

View File

@@ -135,10 +135,12 @@ As result a link:#server-info[ServerInfo] entity is returned.
Lists the caches of the server. Caches defined by plugins are included.
The caller must be a member of a group that is granted the
link:access-control.html#capability_viewCaches[View Caches] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_viewCaches[View Caches]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
As result a map of link:#cache-info[CacheInfo] entities is returned.
@@ -493,10 +495,12 @@ link:#cache-operation-input[CacheOperationInput] entity.
Retrieves information about a cache.
The caller must be a member of a group that is granted the
link:access-control.html#capability_viewCaches[View Caches] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_viewCaches[View Caches]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
As result a link:#cache-info[CacheInfo] entity is returned.
@@ -532,15 +536,15 @@ As result a link:#cache-info[CacheInfo] entity is returned.
Flushes a cache.
The caller must be a member of a group that is granted the
link:access-control.html#capability_flushCaches[Flush Caches] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
The caller must be a member of a group that is granted one of the
following capabilities:
The "web_sessions" cache can only be flushed if the caller is member of
a group that is granted the
link:access-control.html#capability_administrateServer[Administrate
Server] capability.
* link:access-control.html#capability_flushCaches[Flush Caches] (any cache
except "web_sessions")
* link:access-control.html#capability_maintainServer[Maintain Server] (any cache
including "web_sessions")
* link:access-control.html#capability_administrateServer[Administrate Server]
(any cache including "web_sessions")
.Request
----
@@ -749,10 +753,12 @@ End-users may see a task only if they can also see the project the task
is associated with. Tasks operating on other projects, or that do not
have a specific project, are hidden.
Members of a group that is granted the
link:access-control.html#capability_viewQueue[View Queue] capability or
the link:access-control.html#capability_administrateServer[Administrate
Server] capability can see all tasks.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_viewQueue[View Queue]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
As result a list of link:#task-info[TaskInfo] entities is returned.
@@ -801,10 +807,12 @@ End-users may see a task only if they can also see the project the task
is associated with. Tasks operating on other projects, or that do not
have a specific project, are hidden.
Members of a group that is granted the
link:access-control.html#capability_viewQueue[View Queue] capability or
the link:access-control.html#capability_administrateServer[Administrate
Server] capability can see all tasks.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_viewQueue[View Queue]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
As result a link:#task-info[TaskInfo] entity is returned.
@@ -837,19 +845,23 @@ As result a link:#task-info[TaskInfo] entity is returned.
Kills a task from the background work queue that the Gerrit daemon
is currently performing, or will perform in the near future.
The caller must be a member of a group that is granted the
link:access-control.html#capability_kill[Kill Task] capability
or the link:access-control.html#capability_administrateServer[
Administrate Server] capability.
The caller must be a member of a group that is granted one of the
following capabilities:
* link:access-control.html#capability_kill[Kill Task]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
End-users may see a task only if they can also see the project the task
is associated with. Tasks operating on other projects, or that do not
have a specific project, are hidden.
Members of a group that is granted the
link:access-control.html#capability_viewQueue[View Queue] capability or
the link:access-control.html#capability_administrateServer[Administrate
Server] capability can see all tasks.
Members of a group granted one of the following capabilities may view
all tasks:
* link:access-control.html#capability_viewQueue[View Queue]
* link:access-control.html#capability_maintainServer[Maintain Server]
* link:access-control.html#capability_administrateServer[Administrate Server]
.Request
----

View File

@@ -24,6 +24,7 @@ class CapabilityInfo {
public boolean emailReviewers;
public boolean flushCaches;
public boolean killTask;
public boolean maintainServer;
public boolean modifyAccount;
public boolean priority;
public QueryLimit queryLimit;

View File

@@ -26,7 +26,7 @@ public class IndexChangeIT extends AbstractDaemonTest {
@Test
public void indexChange() throws Exception {
String changeId = createChange().getChangeId();
RestResponse r = userSession.post("/changes/" + changeId + "/index/");
RestResponse r = adminSession.post("/changes/" + changeId + "/index/");
assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT);
}

View File

@@ -82,7 +82,8 @@ public class GarbageCollectionIT extends AbstractDaemonTest {
public void testGcWithoutCapability_Error() throws Exception {
SshSession s = new SshSession(server, user);
s.exec("gerrit gc --all");
assertError("Capability runGC is required to access this resource", s.getError());
assertError("One of the following capabilities is required to access this"
+ " resource: [runGC, maintainServer]", s.getError());
s.close();
}

View File

@@ -71,6 +71,16 @@ public class GlobalCapability {
/** Can terminate any task using the kill command. */
public static final String KILL_TASK = "killTask";
/**
* Can perform limited server maintenance.
* <p>
* Includes tasks such as reindexing changes and flushing caches that may need
* to be performed regularly. Does <strong>not</strong> grant arbitrary
* read/write/ACL management permissions as does {@link
* #ADMINISTRATE_SERVER}.
*/
public static final String MAINTAIN_SERVER = "maintainServer";
/** Can modify any account on the server. */
public static final String MODIFY_ACCOUNT = "modifyAccount";
@@ -121,6 +131,7 @@ public class GlobalCapability {
NAMES_ALL.add(EMAIL_REVIEWERS);
NAMES_ALL.add(FLUSH_CACHES);
NAMES_ALL.add(KILL_TASK);
NAMES_ALL.add(MAINTAIN_SERVER);
NAMES_ALL.add(MODIFY_ACCOUNT);
NAMES_ALL.add(PRIORITY);
NAMES_ALL.add(QUERY_LIMIT);

View File

@@ -107,7 +107,7 @@ public class CapabilityControl {
/** @return true if the user can kill any running task. */
public boolean canKillTask() {
return canPerform(GlobalCapability.KILL_TASK)
|| canAdministrateServer();
|| canMaintainServer();
}
/** @return true if the user can modify an account for another user. */
@@ -125,12 +125,18 @@ public class CapabilityControl {
/** @return true if the user can view the server caches. */
public boolean canViewCaches() {
return canPerform(GlobalCapability.VIEW_CACHES)
|| canAdministrateServer();
|| canMaintainServer();
}
/** @return true if the user can flush the server's caches. */
public boolean canFlushCaches() {
return canPerform(GlobalCapability.FLUSH_CACHES)
|| canMaintainServer();
}
/** @return true if the user can perform basic server maintenance. */
public boolean canMaintainServer() {
return canPerform(GlobalCapability.MAINTAIN_SERVER)
|| canAdministrateServer();
}
@@ -149,7 +155,7 @@ public class CapabilityControl {
/** @return true if the user can view the entire queue. */
public boolean canViewQueue() {
return canPerform(GlobalCapability.VIEW_QUEUE)
|| canAdministrateServer();
|| canMaintainServer();
}
/** @return true if the user can access the database (with gsql). */
@@ -166,7 +172,7 @@ public class CapabilityControl {
/** @return true if the user can run the Git garbage collection. */
public boolean canRunGC() {
return canPerform(GlobalCapability.RUN_GC)
|| canAdministrateServer();
|| canMaintainServer();
}
/** @return true if the user can impersonate another user. */

View File

@@ -21,6 +21,7 @@ import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
import static com.google.gerrit.common.data.GlobalCapability.EMAIL_REVIEWERS;
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.MODIFY_ACCOUNT;
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
@@ -115,6 +116,7 @@ class GetCapabilities implements RestReadView<AccountResource> {
have.put(EMAIL_REVIEWERS, cc.canEmailReviewers());
have.put(FLUSH_CACHES, cc.canFlushCaches());
have.put(KILL_TASK, cc.canKillTask());
have.put(MAINTAIN_SERVER, cc.canMaintainServer());
have.put(MODIFY_ACCOUNT, cc.canModifyAccount());
have.put(RUN_GC, cc.canRunGC());
have.put(STREAM_EVENTS, cc.canStreamEvents());

View File

@@ -46,8 +46,8 @@ public class Check implements RestReadView<ChangeResource>,
ChangeControl ctl = rsrc.getControl();
if (!ctl.isOwner()
&& !ctl.getProjectControl().isOwner()
&& !ctl.getCurrentUser().getCapabilities().canAdministrateServer()) {
throw new AuthException("Not owner");
&& !ctl.getCurrentUser().getCapabilities().canMaintainServer()) {
throw new AuthException("Cannot fix change");
}
return Response.withMustRevalidate(json.fix(input).format(rsrc));
}

View File

@@ -14,11 +14,13 @@
package com.google.gerrit.server.change;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.change.Index.Input;
import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -41,7 +43,14 @@ public class Index implements RestModifyView<ChangeResource, Input> {
}
@Override
public Response<?> apply(ChangeResource rsrc, Input input) throws IOException {
public Response<?> apply(ChangeResource rsrc, Input input)
throws IOException, AuthException {
ChangeControl ctl = rsrc.getControl();
if (!ctl.isOwner()
&& !ctl.getCurrentUser().getCapabilities().canMaintainServer()) {
throw new AuthException(
"Only change owner or server maintainer can reindex");
}
indexer.index(db.get(), rsrc.getChange());
return Response.none();
}

View File

@@ -14,9 +14,11 @@
package com.google.gerrit.server.config;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
import com.google.common.cache.Cache;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AcceptsPost;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -31,7 +33,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@RequiresAnyCapability({VIEW_CACHES, MAINTAIN_SERVER})
@Singleton
public class CachesCollection implements
ChildCollection<ConfigResource, CacheResource>, AcceptsPost<ConfigResource> {

View File

@@ -31,6 +31,7 @@ public class CapabilityConstants extends TranslationBundle {
public String emailReviewers;
public String flushCaches;
public String killTask;
public String maintainServer;
public String modifyAccount;
public String priority;
public String queryLimit;

View File

@@ -14,15 +14,17 @@
package com.google.gerrit.server.config;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.server.config.DeleteTask.Input;
import com.google.inject.Singleton;
@Singleton
@RequiresCapability(GlobalCapability.KILL_TASK)
@RequiresAnyCapability({KILL_TASK, MAINTAIN_SERVER})
public class DeleteTask implements RestModifyView<TaskResource, Input> {
public static class Input {
}

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.server.config;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -25,7 +27,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@RequiresCapability(GlobalCapability.FLUSH_CACHES)
@RequiresAnyCapability({FLUSH_CACHES, MAINTAIN_SERVER})
@Singleton
public class FlushCache implements RestModifyView<CacheResource, Input> {
public static class Input {
@@ -44,9 +46,9 @@ public class FlushCache implements RestModifyView<CacheResource, Input> {
public Response<String> apply(CacheResource rsrc, Input input)
throws AuthException {
if (WEB_SESSIONS.equals(rsrc.getName())
&& !self.get().getCapabilities().canAdministrateServer()) {
&& !self.get().getCapabilities().canMaintainServer()) {
throw new AuthException(String.format(
"only site administrators can flush %s", WEB_SESSIONS));
"only site maintainers can flush %s", WEB_SESSIONS));
}
rsrc.getCache().invalidateAll();

View File

@@ -40,7 +40,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@RequiresCapability(GlobalCapability.MAINTAIN_SERVER)
public class GetSummary implements RestReadView<ConfigResource> {
private final WorkQueue workQueue;

View File

@@ -14,14 +14,15 @@
package com.google.gerrit.server.config;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
import static com.google.gerrit.server.config.CacheResource.cacheNameOf;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -36,7 +37,7 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@RequiresAnyCapability({VIEW_CACHES, MAINTAIN_SERVER})
public class ListCaches implements RestReadView<ConfigResource> {
private final DynamicMap<Cache<?, ?>> cacheMap;

View File

@@ -14,9 +14,11 @@
package com.google.gerrit.server.config;
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.common.cache.Cache;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -31,7 +33,7 @@ import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
@RequiresCapability(GlobalCapability.FLUSH_CACHES)
@RequiresAnyCapability({FLUSH_CACHES, MAINTAIN_SERVER})
@Singleton
public class PostCaches implements RestModifyView<ConfigResource, Input> {
public static class Input {

View File

@@ -7,6 +7,7 @@ createProject = Create Project
emailReviewers = Email Reviewers
flushCaches = Flush Caches
killTask = Kill Task
maintainServer = Maintain Server
modifyAccount = Modify Account
priority = Priority
queryLimit = Query Limit

View File

@@ -14,12 +14,13 @@
package com.google.gerrit.sshd.commands;
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.server.config.PostCaches.Operation.FLUSH;
import static com.google.gerrit.server.config.PostCaches.Operation.FLUSH_ALL;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.ListCaches;
@@ -36,7 +37,7 @@ import java.util.ArrayList;
import java.util.List;
/** Causes the caches to purge all entries and reload. */
@RequiresCapability(GlobalCapability.FLUSH_CACHES)
@RequiresAnyCapability({FLUSH_CACHES, MAINTAIN_SERVER})
@CommandMetaData(name = "flush-caches", description = "Flush some/all server caches from memory",
runsAt = MASTER_OR_SLAVE)
final class FlushCaches extends SshCommand {

View File

@@ -14,12 +14,13 @@
package com.google.gerrit.sshd.commands;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GarbageCollectionResult;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.project.ProjectCache;
@@ -35,7 +36,7 @@ import java.util.ArrayList;
import java.util.List;
/** Runs the Git garbage collection. */
@RequiresCapability(GlobalCapability.RUN_GC)
@RequiresAnyCapability({RUN_GC, MAINTAIN_SERVER})
@CommandMetaData(name = "gc", description = "Run Git garbage collection",
runsAt = MASTER_OR_SLAVE)
public class GarbageCollectionCommand extends SshCommand {

View File

@@ -14,8 +14,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -34,7 +36,7 @@ import java.util.List;
/** Kill a task in the work queue. */
@AdminHighPriorityCommand
@RequiresCapability(GlobalCapability.KILL_TASK)
@RequiresAnyCapability({KILL_TASK, MAINTAIN_SERVER})
final class KillCommand extends SshCommand {
@Inject
private TasksCollection tasksCollection;

View File

@@ -14,13 +14,14 @@
package com.google.gerrit.sshd.commands;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.base.Strings;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
@@ -53,7 +54,7 @@ import java.util.Map;
import java.util.Map.Entry;
/** Show the current cache states. */
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@RequiresAnyCapability({VIEW_CACHES, MAINTAIN_SERVER})
@CommandMetaData(name = "show-caches", description = "Display current cache statistics",
runsAt = MASTER_OR_SLAVE)
final class ShowCaches extends SshCommand {
@@ -154,7 +155,7 @@ final class ShowCaches extends SshCommand {
printDiskCaches(caches);
stdout.print('\n');
if (self.get().getCapabilities().canAdministrateServer()) {
if (self.get().getCapabilities().canMaintainServer()) {
sshSummary();
SummaryInfo summary =