Convert administrateServer to PermissionBackend

Leave a poorly named backdoor in CapabilityControl for the existing
ProjectControl, RefControl, ChangeControl and GroupControl to test
administrator permission.

Update test expecting a failure when administateServer is not granted.

Change-Id: I0f523dbf26506ea53c38ffb02aeef74f3cf18ba6
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 839f166..8695498 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -218,7 +218,7 @@
 
     setApiUser(user);
     exception.expect(AuthException.class);
-    exception.expectMessage("not administrator");
+    exception.expectMessage("administrate server not permitted");
     gApi.projects().name(newProjectName).access(accessInput);
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
index 84348d0..93673bc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtexpui.server.CacheHeaders;
 import com.google.gwtjsonrpc.server.JsonServlet;
 import com.google.gwtjsonrpc.server.RPCServletUtils;
@@ -220,7 +221,7 @@
   private DiffPreferencesInfo getDiffPreferences(IdentifiedUser user) {
     try {
       return getDiff.apply(new AccountResource(user));
-    } catch (AuthException | ConfigInvalidException | IOException e) {
+    } catch (AuthException | ConfigInvalidException | IOException | PermissionBackendException e) {
       log.warn("Cannot query account diff preferences", e);
     }
     return DiffPreferencesInfo.defaults();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 0d90190..8d0947c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.project.RefPattern;
@@ -95,7 +96,7 @@
   public final T call()
       throws NoSuchProjectException, IOException, ConfigInvalidException, InvalidNameException,
           NoSuchGroupException, OrmException, UpdateParentFailedException,
-          PermissionDeniedException {
+          PermissionDeniedException, PermissionBackendException {
     final ProjectControl projectControl = projectControlFactory.controlFor(projectName);
 
     Capable r = projectControl.canPushToAtLeastOneRef();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
index 8c10c73..1c5495f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AddSshKey.java
@@ -30,6 +30,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AddSshKey.Input;
 import com.google.gerrit.server.mail.send.AddKeySender;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.ssh.SshKeyCache;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -50,6 +53,7 @@
   }
 
   private final Provider<CurrentUser> self;
+  private final PermissionBackend permissionBackend;
   private final VersionedAuthorizedKeys.Accessor authorizedKeys;
   private final SshKeyCache sshKeyCache;
   private final AddKeySender.Factory addKeyFactory;
@@ -57,10 +61,12 @@
   @Inject
   AddSshKey(
       Provider<CurrentUser> self,
+      PermissionBackend permissionBackend,
       VersionedAuthorizedKeys.Accessor authorizedKeys,
       SshKeyCache sshKeyCache,
       AddKeySender.Factory addKeyFactory) {
     this.self = self;
+    this.permissionBackend = permissionBackend;
     this.authorizedKeys = authorizedKeys;
     this.sshKeyCache = sshKeyCache;
     this.addKeyFactory = addKeyFactory;
@@ -68,9 +74,10 @@
 
   @Override
   public Response<SshKeyInfo> apply(AccountResource rsrc, Input input)
-      throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not allowed to add SSH keys");
+      throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
+          PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
     return apply(rsrc.getUser(), input);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
index f38d019..2bba536 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CapabilityControl.java
@@ -30,7 +30,10 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.PluginPermission;
+import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.RefControl;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
@@ -65,8 +68,23 @@
     return user;
   }
 
-  /** @return true if the user can administer this server. */
-  public boolean canAdministrateServer() {
+  /**
+   * <b>Do not use.</b> Determine if the user can administer this server.
+   *
+   * <p>This method is visible only for the benefit of the following transitional classes:
+   *
+   * <ul>
+   *   <li>{@link ProjectControl}
+   *   <li>{@link RefControl}
+   *   <li>{@link ChangeControl}
+   *   <li>{@link GroupControl}
+   * </ul>
+   *
+   * Other callers should not use this method, as it is slated to go away.
+   *
+   * @return true if the user can administer this server.
+   */
+  public boolean isAdmin_DoNotUse() {
     if (canAdministrateServer == null) {
       if (user.getRealUser() != user) {
         canAdministrateServer = false;
@@ -91,7 +109,7 @@
 
   /** @return true if the user can view all accounts. */
   public boolean canViewAllAccounts() {
-    return canPerform(GlobalCapability.VIEW_ALL_ACCOUNTS) || canAdministrateServer();
+    return canPerform(GlobalCapability.VIEW_ALL_ACCOUNTS) || isAdmin_DoNotUse();
   }
 
   /** @return true if the user can access the database (with gsql). */
@@ -220,7 +238,7 @@
     if (perm instanceof GlobalPermission) {
       return can((GlobalPermission) perm);
     } else if (perm instanceof PluginPermission) {
-      return canPerform(perm.permissionName()) || canAdministrateServer();
+      return canPerform(perm.permissionName()) || isAdmin_DoNotUse();
     }
     throw new PermissionBackendException(perm + " unsupported");
   }
@@ -228,7 +246,7 @@
   private boolean can(GlobalPermission perm) throws PermissionBackendException {
     switch (perm) {
       case ADMINISTRATE_SERVER:
-        return canAdministrateServer();
+        return isAdmin_DoNotUse();
       case EMAIL_REVIEWERS:
         return canEmailReviewers();
       case VIEW_ALL_ACCOUNTS:
@@ -241,7 +259,7 @@
       case VIEW_QUEUE:
         return canPerform(perm.permissionName())
             || canPerform(GlobalCapability.MAINTAIN_SERVER)
-            || canAdministrateServer();
+            || isAdmin_DoNotUse();
 
       case CREATE_ACCOUNT:
       case CREATE_GROUP:
@@ -251,7 +269,7 @@
       case STREAM_EVENTS:
       case VIEW_CONNECTIONS:
       case VIEW_PLUGINS:
-        return canPerform(perm.permissionName()) || canAdministrateServer();
+        return canPerform(perm.permissionName()) || isAdmin_DoNotUse();
 
       case ACCESS_DATABASE:
       case RUN_AS:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
index 3d5d38e..f1ecd29 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteSshKey.java
@@ -19,6 +19,9 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.DeleteSshKey.Input;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.ssh.SshKeyCache;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -33,15 +36,18 @@
   public static class Input {}
 
   private final Provider<CurrentUser> self;
+  private final PermissionBackend permissionBackend;
   private final VersionedAuthorizedKeys.Accessor authorizedKeys;
   private final SshKeyCache sshKeyCache;
 
   @Inject
   DeleteSshKey(
       Provider<CurrentUser> self,
+      PermissionBackend permissionBackend,
       VersionedAuthorizedKeys.Accessor authorizedKeys,
       SshKeyCache sshKeyCache) {
     this.self = self;
+    this.permissionBackend = permissionBackend;
     this.authorizedKeys = authorizedKeys;
     this.sshKeyCache = sshKeyCache;
   }
@@ -49,9 +55,9 @@
   @Override
   public Response<?> apply(AccountResource.SshKey rsrc, Input input)
       throws AuthException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not allowed to delete SSH keys");
+          ConfigInvalidException, PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     authorizedKeys.deleteKey(rsrc.getUser().getAccountId(), rsrc.getSshKey().getKey().get());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
index 97102a2..1666eb1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
@@ -25,6 +25,9 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -37,13 +40,18 @@
 public class DeleteWatchedProjects
     implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
   private final Provider<IdentifiedUser> self;
+  private final PermissionBackend permissionBackend;
   private final AccountCache accountCache;
   private final WatchConfig.Accessor watchConfig;
 
   @Inject
   DeleteWatchedProjects(
-      Provider<IdentifiedUser> self, AccountCache accountCache, WatchConfig.Accessor watchConfig) {
+      Provider<IdentifiedUser> self,
+      PermissionBackend permissionBackend,
+      AccountCache accountCache,
+      WatchConfig.Accessor watchConfig) {
     this.self = self;
+    this.permissionBackend = permissionBackend;
     this.accountCache = accountCache;
     this.watchConfig = watchConfig;
   }
@@ -51,9 +59,9 @@
   @Override
   public Response<?> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
       throws AuthException, UnprocessableEntityException, OrmException, IOException,
-          ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("It is not allowed to edit project watches of other users");
+          ConfigInvalidException, PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
     if (input == null) {
       return Response.none();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
index b894f56..e31f481 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
@@ -17,12 +17,16 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AcceptsCreate;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ChildCollection;
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.AccountResource.Email;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -34,6 +38,7 @@
   private final DynamicMap<RestView<AccountResource.Email>> views;
   private final GetEmails list;
   private final Provider<CurrentUser> self;
+  private final PermissionBackend permissionBackend;
   private final CreateEmail.Factory createEmailFactory;
 
   @Inject
@@ -41,10 +46,12 @@
       DynamicMap<RestView<AccountResource.Email>> views,
       GetEmails list,
       Provider<CurrentUser> self,
+      PermissionBackend permissionBackend,
       CreateEmail.Factory createEmailFactory) {
     this.views = views;
     this.list = list;
     this.self = self;
+    this.permissionBackend = permissionBackend;
     this.createEmailFactory = createEmailFactory;
   }
 
@@ -55,21 +62,21 @@
 
   @Override
   public AccountResource.Email parse(AccountResource rsrc, IdString id)
-      throws ResourceNotFoundException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new ResourceNotFoundException();
+      throws ResourceNotFoundException, PermissionBackendException, AuthException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     if ("preferred".equals(id.get())) {
       String email = rsrc.getUser().getAccount().getPreferredEmail();
       if (Strings.isNullOrEmpty(email)) {
-        throw new ResourceNotFoundException();
+        throw new ResourceNotFoundException(id);
       }
       return new AccountResource.Email(rsrc.getUser(), email);
     } else if (rsrc.getUser().hasEmailAddress(id.get())) {
       return new AccountResource.Email(rsrc.getUser(), id.get());
     } else {
-      throw new ResourceNotFoundException();
+      throw new ResourceNotFoundException(id);
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
index 0edff4f..8215c6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
@@ -25,6 +25,9 @@
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.UserConfigSections;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -42,23 +45,26 @@
 
   private final Provider<CurrentUser> self;
   private final Provider<AllUsersName> allUsersName;
+  private final PermissionBackend permissionBackend;
   private final GitRepositoryManager gitMgr;
 
   @Inject
   GetDiffPreferences(
       Provider<CurrentUser> self,
       Provider<AllUsersName> allUsersName,
+      PermissionBackend permissionBackend,
       GitRepositoryManager gitMgr) {
     this.self = self;
     this.allUsersName = allUsersName;
+    this.permissionBackend = permissionBackend;
     this.gitMgr = gitMgr;
   }
 
   @Override
   public DiffPreferencesInfo apply(AccountResource rsrc)
-      throws AuthException, ConfigInvalidException, IOException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("restricted to administrator");
+      throws AuthException, ConfigInvalidException, IOException, PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     Account.Id id = rsrc.getUser().getAccountId();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
index e0aeee0..c2c0547 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetWatchedProjects.java
@@ -23,6 +23,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -38,22 +41,28 @@
 
 @Singleton
 public class GetWatchedProjects implements RestReadView<AccountResource> {
-
+  private final PermissionBackend permissionBackend;
   private final Provider<IdentifiedUser> self;
   private final WatchConfig.Accessor watchConfig;
 
   @Inject
-  public GetWatchedProjects(Provider<IdentifiedUser> self, WatchConfig.Accessor watchConfig) {
+  public GetWatchedProjects(
+      PermissionBackend permissionBackend,
+      Provider<IdentifiedUser> self,
+      WatchConfig.Accessor watchConfig) {
+    this.permissionBackend = permissionBackend;
     this.self = self;
     this.watchConfig = watchConfig;
   }
 
   @Override
   public List<ProjectWatchInfo> apply(AccountResource rsrc)
-      throws OrmException, AuthException, IOException, ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("It is not allowed to list project watches of other users");
+      throws OrmException, AuthException, IOException, ConfigInvalidException,
+          PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
+
     Account.Id accountId = rsrc.getUser().getAccountId();
     List<ProjectWatchInfo> projectWatchInfos = new ArrayList<>();
     for (Map.Entry<ProjectWatchKey, Set<NotifyType>> e :
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
index ee788ec..e88e97e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -127,7 +127,7 @@
     return user.isInternalUser()
         || groupBackend.isVisibleToAll(group.getGroupUUID())
         || user.getEffectiveGroups().contains(group.getGroupUUID())
-        || user.getCapabilities().canAdministrateServer()
+        || user.getCapabilities().isAdmin_DoNotUse()
         || isOwner();
   }
 
@@ -139,7 +139,7 @@
       AccountGroup.UUID ownerUUID = accountGroup.getOwnerGroupUUID();
       isOwner =
           getUser().getEffectiveGroups().contains(ownerUUID)
-              || getUser().getCapabilities().canAdministrateServer();
+              || getUser().getCapabilities().isAdmin_DoNotUse();
     }
     return isOwner;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
index 55ba912..d80c40b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PostWatchedProjects.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.account;
 
 import com.google.gerrit.extensions.client.ProjectWatchInfo;
-import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -24,6 +23,9 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.WatchConfig.NotifyType;
 import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectsCollection;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -41,6 +43,7 @@
 public class PostWatchedProjects
     implements RestModifyView<AccountResource, List<ProjectWatchInfo>> {
   private final Provider<IdentifiedUser> self;
+  private final PermissionBackend permissionBackend;
   private final GetWatchedProjects getWatchedProjects;
   private final ProjectsCollection projectsCollection;
   private final AccountCache accountCache;
@@ -49,11 +52,13 @@
   @Inject
   public PostWatchedProjects(
       Provider<IdentifiedUser> self,
+      PermissionBackend permissionBackend,
       GetWatchedProjects getWatchedProjects,
       ProjectsCollection projectsCollection,
       AccountCache accountCache,
       WatchConfig.Accessor watchConfig) {
     this.self = self;
+    this.permissionBackend = permissionBackend;
     this.getWatchedProjects = getWatchedProjects;
     this.projectsCollection = projectsCollection;
     this.accountCache = accountCache;
@@ -62,10 +67,12 @@
 
   @Override
   public List<ProjectWatchInfo> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
-      throws OrmException, RestApiException, IOException, ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not allowed to edit project watches");
+      throws OrmException, RestApiException, IOException, ConfigInvalidException,
+          PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
+
     Account.Id accountId = rsrc.getUser().getAccountId();
     watchConfig.upsertProjectWatches(accountId, asMap(input));
     accountCache.evict(accountId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
index d8451bf..c06e5a3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutHttpPassword.java
@@ -29,6 +29,9 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.account.externalids.ExternalIdsUpdate;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -57,6 +60,7 @@
 
   private final Provider<CurrentUser> self;
   private final Provider<ReviewDb> dbProvider;
+  private final PermissionBackend permissionBackend;
   private final AccountCache accountCache;
   private final ExternalIds externalIds;
   private final ExternalIdsUpdate.User externalIdsUpdate;
@@ -65,11 +69,13 @@
   PutHttpPassword(
       Provider<CurrentUser> self,
       Provider<ReviewDb> dbProvider,
+      PermissionBackend permissionBackend,
       AccountCache accountCache,
       ExternalIds externalIds,
       ExternalIdsUpdate.User externalIdsUpdate) {
     this.self = self;
     this.dbProvider = dbProvider;
+    this.permissionBackend = permissionBackend;
     this.accountCache = accountCache;
     this.externalIds = externalIds;
     this.externalIdsUpdate = externalIdsUpdate;
@@ -78,7 +84,11 @@
   @Override
   public Response<String> apply(AccountResource rsrc, Input input)
       throws AuthException, ResourceNotFoundException, ResourceConflictException, OrmException,
-          IOException, ConfigInvalidException {
+          IOException, ConfigInvalidException, PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+    }
+
     if (input == null) {
       input = new Input();
     }
@@ -86,22 +96,12 @@
 
     String newPassword;
     if (input.generate) {
-      if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-        throw new AuthException("not allowed to generate HTTP password");
-      }
       newPassword = generate();
-
     } else if (input.httpPassword == null) {
-      if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-        throw new AuthException("not allowed to clear HTTP password");
-      }
       newPassword = null;
     } else {
-      if (!self.get().getCapabilities().canAdministrateServer()) {
-        throw new AuthException(
-            "not allowed to set HTTP password directly, "
-                + "requires the Administrate Server permission");
-      }
+      // Only administrators can explicitly set the password.
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
       newPassword = input.httpPassword;
     }
     return apply(rsrc.getUser(), newPassword);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
index e3a3c12..57bff65 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutUsername.java
@@ -25,6 +25,9 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.PutUsername.Input;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -40,6 +43,7 @@
 
   private final Provider<CurrentUser> self;
   private final ChangeUserName.Factory changeUserNameFactory;
+  private final PermissionBackend permissionBackend;
   private final Realm realm;
   private final Provider<ReviewDb> db;
 
@@ -47,10 +51,12 @@
   PutUsername(
       Provider<CurrentUser> self,
       ChangeUserName.Factory changeUserNameFactory,
+      PermissionBackend permissionBackend,
       Realm realm,
       Provider<ReviewDb> db) {
     this.self = self;
     this.changeUserNameFactory = changeUserNameFactory;
+    this.permissionBackend = permissionBackend;
     this.realm = realm;
     this.db = db;
   }
@@ -58,9 +64,10 @@
   @Override
   public String apply(AccountResource rsrc, Input input)
       throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
-          ResourceConflictException, OrmException, IOException, ConfigInvalidException {
-    if (self.get() != rsrc.getUser() && !self.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not allowed to set username");
+          ResourceConflictException, OrmException, IOException, ConfigInvalidException,
+          PermissionBackendException {
+    if (self.get() != rsrc.getUser()) {
+      permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     if (!realm.allowsEdit(AccountFieldName.USER_NAME)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index dc5ea4c..6604496 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -255,7 +255,7 @@
   public DiffPreferencesInfo getDiffPreferences() throws RestApiException {
     try {
       return getDiffPreferences.apply(account);
-    } catch (IOException | ConfigInvalidException e) {
+    } catch (IOException | ConfigInvalidException | PermissionBackendException e) {
       throw new RestApiException("Cannot query diff preferences", e);
     }
   }
@@ -291,7 +291,7 @@
   public List<ProjectWatchInfo> getWatchedProjects() throws RestApiException {
     try {
       return getWatchedProjects.apply(account);
-    } catch (OrmException | IOException | ConfigInvalidException e) {
+    } catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
       throw new RestApiException("Cannot get watched projects", e);
     }
   }
@@ -301,7 +301,7 @@
       throws RestApiException {
     try {
       return postWatchedProjects.apply(account, in);
-    } catch (OrmException | IOException | ConfigInvalidException e) {
+    } catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
       throw new RestApiException("Cannot update watched projects", e);
     }
   }
@@ -310,7 +310,7 @@
   public void deleteWatchedProjects(List<ProjectWatchInfo> in) throws RestApiException {
     try {
       deleteWatchedProjects.apply(account, in);
-    } catch (OrmException | IOException | ConfigInvalidException e) {
+    } catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
       throw new RestApiException("Cannot delete watched projects", e);
     }
   }
@@ -421,7 +421,7 @@
     in.raw = RawInputUtil.create(key);
     try {
       return addSshKey.apply(account, in).value();
-    } catch (OrmException | IOException | ConfigInvalidException e) {
+    } catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
       throw new RestApiException("Cannot add SSH key", e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 65673de..7d283ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -300,7 +300,7 @@
   public ProjectAccessInfo access(ProjectAccessInput p) throws RestApiException {
     try {
       return setAccess.apply(checkExists(), p);
-    } catch (IOException e) {
+    } catch (IOException | PermissionBackendException e) {
       throw new RestApiException("Cannot put access rights", e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index b10cc71..86e55fe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -105,6 +105,8 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
@@ -292,6 +294,7 @@
   private final Provider<InternalChangeQuery> queryProvider;
   private final ChangeNotes.Factory notesFactory;
   private final AccountResolver accountResolver;
+  private final PermissionBackend permissionBackend;
   private final CmdLineParser.Factory optionParserFactory;
   private final GitReferenceUpdated gitRefUpdated;
   private final PatchSetInfoFactory patchSetInfoFactory;
@@ -357,6 +360,7 @@
       Provider<InternalChangeQuery> queryProvider,
       ChangeNotes.Factory notesFactory,
       AccountResolver accountResolver,
+      PermissionBackend permissionBackend,
       CmdLineParser.Factory optionParserFactory,
       GitReferenceUpdated gitRefUpdated,
       PatchSetInfoFactory patchSetInfoFactory,
@@ -396,6 +400,7 @@
     this.queryProvider = queryProvider;
     this.notesFactory = notesFactory;
     this.accountResolver = accountResolver;
+    this.permissionBackend = permissionBackend;
     this.optionParserFactory = optionParserFactory;
     this.gitRefUpdated = gitRefUpdated;
     this.patchSetInfoFactory = patchSetInfoFactory;
@@ -1042,10 +1047,13 @@
                   continue;
                 }
               } else {
-                if (!oldParent.equals(newParent)
-                    && !user.getCapabilities().canAdministrateServer()) {
-                  reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
-                  continue;
+                if (!oldParent.equals(newParent)) {
+                  try {
+                    permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+                  } catch (AuthException e) {
+                    reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
+                    continue;
+                  }
                 }
 
                 if (projectCache.get(newParent) == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/MergeValidators.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 150965c..298c650 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.DynamicMap.Entry;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
@@ -29,6 +30,9 @@
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
@@ -36,8 +40,12 @@
 import java.util.List;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class MergeValidators {
+  private static final Logger log = LoggerFactory.getLogger(MergeValidators.class);
+
   private final DynamicSet<MergeValidationListener> mergeValidationListeners;
   private final ProjectConfigValidator.Factory projectConfigValidatorFactory;
 
@@ -93,6 +101,7 @@
 
     private final AllProjectsName allProjectsName;
     private final ProjectCache projectCache;
+    private final PermissionBackend permissionBackend;
     private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
 
     public interface Factory {
@@ -103,9 +112,11 @@
     public ProjectConfigValidator(
         AllProjectsName allProjectsName,
         ProjectCache projectCache,
+        PermissionBackend permissionBackend,
         DynamicMap<ProjectConfigEntry> pluginConfigEntries) {
       this.allProjectsName = allProjectsName;
       this.projectCache = projectCache;
+      this.permissionBackend = permissionBackend;
       this.pluginConfigEntries = pluginConfigEntries;
     }
 
@@ -132,8 +143,13 @@
             }
           } else {
             if (!oldParent.equals(newParent)) {
-              if (!caller.getCapabilities().canAdministrateServer()) {
+              try {
+                permissionBackend.user(caller).check(GlobalPermission.ADMINISTRATE_SERVER);
+              } catch (AuthException e) {
                 throw new MergeValidationException(SET_BY_ADMIN);
+              } catch (PermissionBackendException e) {
+                log.warn("Cannot check ADMINISTRATE_SERVER", e);
+                throw new MergeValidationException("validation unavailable");
               }
 
               if (projectCache.get(newParent) == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
index 9bf14e7..5e3110c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -356,7 +356,6 @@
 
   private List<AccountGroup> filterGroups(Collection<AccountGroup> groups) {
     List<AccountGroup> filteredGroups = new ArrayList<>(groups.size());
-    boolean isAdmin = identifiedUser.get().getCapabilities().canAdministrateServer();
     for (AccountGroup group : groups) {
       if (!Strings.isNullOrEmpty(matchSubstring)) {
         if (!group
@@ -372,13 +371,11 @@
       if (!groupsToInspect.isEmpty() && !groupsToInspect.contains(group.getGroupUUID())) {
         continue;
       }
-      if (!isAdmin) {
-        GroupControl c = groupControlFactory.controlFor(group);
-        if (!c.isVisible()) {
-          continue;
-        }
+
+      GroupControl c = groupControlFactory.controlFor(group);
+      if (c.isVisible()) {
+        filteredGroups.add(group);
       }
-      filteredGroups.add(group);
     }
     Collections.sort(filteredGroups, new GroupComparator());
     return filteredGroups;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AddKeySender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AddKeySender.java
index 0c09639..30938f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -17,9 +17,13 @@
 import com.google.common.base.Joiner;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.mail.Address;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.util.List;
@@ -31,6 +35,7 @@
     AddKeySender create(IdentifiedUser user, List<String> gpgKey);
   }
 
+  private final PermissionBackend permissionBackend;
   private final IdentifiedUser callingUser;
   private final IdentifiedUser user;
   private final AccountSshKey sshKey;
@@ -39,10 +44,12 @@
   @AssistedInject
   public AddKeySender(
       EmailArguments ea,
+      PermissionBackend permissionBackend,
       IdentifiedUser callingUser,
       @Assisted IdentifiedUser user,
       @Assisted AccountSshKey sshKey) {
     super(ea, "addkey");
+    this.permissionBackend = permissionBackend;
     this.callingUser = callingUser;
     this.user = user;
     this.sshKey = sshKey;
@@ -52,10 +59,12 @@
   @AssistedInject
   public AddKeySender(
       EmailArguments ea,
+      PermissionBackend permissionBackend,
       IdentifiedUser callingUser,
       @Assisted IdentifiedUser user,
       @Assisted List<String> gpgKeys) {
     super(ea, "addkey");
+    this.permissionBackend = permissionBackend;
     this.callingUser = callingUser;
     this.user = user;
     this.sshKey = null;
@@ -71,12 +80,25 @@
 
   @Override
   protected boolean shouldSendMessage() {
-    /*
-     * Don't send an email if no keys are added, or an admin is adding a key to
-     * a user.
-     */
-    return (sshKey != null || gpgKeys.size() > 0)
-        && (user.equals(callingUser) || !callingUser.getCapabilities().canAdministrateServer());
+    if (sshKey == null && (gpgKeys == null || gpgKeys.isEmpty())) {
+      // Don't email if no keys were added.
+      return false;
+    }
+
+    if (user.equals(callingUser)) {
+      // Send email if the user self-added a key; this notification is necessary to alert
+      // the user if their account was compromised and a key was unexpectedly added.
+      return true;
+    }
+
+    try {
+      // Don't email if an administrator added a key on behalf of the user.
+      permissionBackend.user(callingUser).check(GlobalPermission.ADMINISTRATE_SERVER);
+      return false;
+    } catch (AuthException | PermissionBackendException e) {
+      // Send email if a non-administrator modified the keys, e.g. by MODIFY_ACCOUNT.
+      return true;
+    }
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 83b9182..180b67d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -85,7 +85,7 @@
   public abstract WithUser user(CurrentUser user);
 
   /** @return lightweight factory scoped to answer for the specified user. */
-  public WithUser user(Provider<CurrentUser> user) {
+  public <U extends CurrentUser> WithUser user(Provider<U> user) {
     return user(checkNotNull(user, "Provider<CurrentUser>").get());
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 1bb1103..8662e4a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -251,7 +251,7 @@
     return (isOwner() // owner (aka creator) of the change can abandon
             || getRefControl().isOwner() // branch owner can abandon
             || getProjectControl().isOwner() // project owner can abandon
-            || getUser().getCapabilities().canAdministrateServer() // site administers are god
+            || getUser().getCapabilities().isAdmin_DoNotUse() // site administers are god
             || getRefControl().canAbandon() // user can abandon a specific ref
         )
         && !isPatchSetLocked(db);
@@ -275,10 +275,13 @@
 
     switch (status) {
       case DRAFT:
-        return isOwner() || getRefControl().canDeleteDrafts() || isAdmin();
+        return isOwner()
+            || getRefControl().canDeleteDrafts()
+            || getUser().getCapabilities().isAdmin_DoNotUse();
       case NEW:
       case ABANDONED:
-        return (isAdmin() || (isOwner() && getRefControl().canDeleteOwnChanges()));
+        return (isOwner() && getRefControl().canDeleteOwnChanges())
+            || getUser().getCapabilities().isAdmin_DoNotUse();
       case MERGED:
       default:
         return false;
@@ -394,10 +397,6 @@
     return false;
   }
 
-  public boolean isAdmin() {
-    return getUser().getCapabilities().canAdministrateServer();
-  }
-
   /** @return true if the user is allowed to remove this reviewer. */
   public boolean canRemoveReviewer(PatchSetApproval approval) {
     return canRemoveReviewer(approval.getAccountId(), approval.getValue());
@@ -424,7 +423,7 @@
       if (getRefControl().canRemoveReviewer() // has removal permissions
           || getRefControl().isOwner() // branch owner
           || getProjectControl().isOwner() // project owner
-          || getUser().getCapabilities().canAdministrateServer()) {
+          || getUser().getCapabilities().isAdmin_DoNotUse()) {
         return true;
       }
     }
@@ -438,7 +437,7 @@
       return isOwner() // owner (aka creator) of the change can edit topic
           || getRefControl().isOwner() // branch owner can edit topic
           || getProjectControl().isOwner() // project owner can edit topic
-          || getUser().getCapabilities().canAdministrateServer() // site administers are god
+          || getUser().getCapabilities().isAdmin_DoNotUse() // site administers are god
           || getRefControl().canEditTopicName() // user can edit topic on a specific ref
       ;
     }
@@ -451,7 +450,7 @@
       return isOwner() // owner (aka creator) of the change can edit desc
           || getRefControl().isOwner() // branch owner can edit desc
           || getProjectControl().isOwner() // project owner can edit desc
-          || getUser().getCapabilities().canAdministrateServer() // site administers are god
+          || getUser().getCapabilities().isAdmin_DoNotUse() // site administers are god
       ;
     }
     return false;
@@ -469,7 +468,7 @@
     return isOwner() // owner (aka creator) of the change can edit hashtags
         || getRefControl().isOwner() // branch owner can edit hashtags
         || getProjectControl().isOwner() // project owner can edit hashtags
-        || getUser().getCapabilities().canAdministrateServer() // site administers are god
+        || getUser().getCapabilities().isAdmin_DoNotUse() // site administers are god
         || getRefControl().canEditHashtags(); // user can edit hashtag on a specific ref
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index b188ee4..241d266 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -324,7 +324,7 @@
   /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
   public boolean isOwner() {
     return (isDeclaredOwner() && !controlForRef("refs/*").isBlocked(Permission.OWNER))
-        || user.getCapabilities().canAdministrateServer();
+        || user.getCapabilities().isAdmin_DoNotUse();
   }
 
   private boolean isDeclaredOwner() {
@@ -337,7 +337,7 @@
 
   /** Does this user have ownership on at least one reference name? */
   public boolean isOwnerAnyRef() {
-    return canPerformOnAnyRef(Permission.OWNER) || user.getCapabilities().canAdministrateServer();
+    return canPerformOnAnyRef(Permission.OWNER) || user.getCapabilities().isAdmin_DoNotUse();
   }
 
   /** @return true if the user can upload to at least one reference */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 50c024e..f9a4b1b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -200,7 +200,7 @@
       // this why for the AllProjects project we allow administrators to push
       // configuration changes if they have push without being project owner.
       if (!(projectControl.getProjectState().isAllProjects()
-          && getUser().getCapabilities().canAdministrateServer())) {
+          && getUser().getCapabilities().isAdmin_DoNotUse())) {
         return false;
       }
     }
@@ -227,7 +227,7 @@
       case UNKNOWN:
       case WEB_BROWSER:
       default:
-        return getUser().getCapabilities().canAdministrateServer()
+        return getUser().getCapabilities().isAdmin_DoNotUse()
             || (isOwner() && !isForceBlocked(Permission.PUSH));
     }
   }
@@ -373,7 +373,7 @@
       case UNKNOWN:
       case WEB_BROWSER:
       default:
-        return getUser().getCapabilities().canAdministrateServer()
+        return getUser().getCapabilities().isAdmin_DoNotUse()
             || (isOwner() && !isForceBlocked(Permission.PUSH))
             || canPushWithForce()
             || canPerform(Permission.DELETE);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
index c74efc6..6c55c37 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
@@ -41,6 +41,9 @@
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
 import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -54,6 +57,7 @@
 @Singleton
 public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessInput> {
   protected final GroupBackend groupBackend;
+  private final PermissionBackend permissionBackend;
   private final GroupsCollection groupsCollection;
   private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
   private final AllProjectsName allProjects;
@@ -65,6 +69,7 @@
   @Inject
   private SetAccess(
       GroupBackend groupBackend,
+      PermissionBackend permissionBackend,
       Provider<MetaDataUpdate.User> metaDataUpdateFactory,
       AllProjectsName allProjects,
       Provider<SetParent> setParent,
@@ -73,6 +78,7 @@
       GetAccess getAccess,
       Provider<IdentifiedUser> identifiedUser) {
     this.groupBackend = groupBackend;
+    this.permissionBackend = permissionBackend;
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.allProjects = allProjects;
     this.setParent = setParent;
@@ -85,7 +91,7 @@
   @Override
   public ProjectAccessInfo apply(ProjectResource rsrc, ProjectAccessInput input)
       throws ResourceNotFoundException, ResourceConflictException, IOException, AuthException,
-          BadRequestException, UnprocessableEntityException {
+          BadRequestException, UnprocessableEntityException, PermissionBackendException {
     List<AccessSection> removals = getAccessSections(input.remove);
     List<AccessSection> additions = getAccessSections(input.add);
     MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
@@ -269,16 +275,11 @@
   }
 
   private void checkGlobalCapabilityPermissions(Project.NameKey projectName)
-      throws BadRequestException, AuthException {
-
+      throws BadRequestException, AuthException, PermissionBackendException {
     if (!allProjects.equals(projectName)) {
       throw new BadRequestException(
           "Cannot edit global capabilities for projects other than " + allProjects.get());
     }
-
-    if (!identifiedUser.get().getCapabilities().canAdministrateServer()) {
-      throw new AuthException(
-          "Editing global capabilities requires " + GlobalCapability.ADMINISTRATE_SERVER);
-    }
+    permissionBackend.user(identifiedUser).check(GlobalPermission.ADMINISTRATE_SERVER);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
index f8d649b..7ec8706 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
@@ -30,6 +30,9 @@
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.MetaDataUpdate;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.SetParent.Input;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -45,12 +48,18 @@
   }
 
   private final ProjectCache cache;
+  private final PermissionBackend permissionBackend;
   private final MetaDataUpdate.Server updateFactory;
   private final AllProjectsName allProjects;
 
   @Inject
-  SetParent(ProjectCache cache, MetaDataUpdate.Server updateFactory, AllProjectsName allProjects) {
+  SetParent(
+      ProjectCache cache,
+      PermissionBackend permissionBackend,
+      MetaDataUpdate.Server updateFactory,
+      AllProjectsName allProjects) {
     this.cache = cache;
+    this.permissionBackend = permissionBackend;
     this.updateFactory = updateFactory;
     this.allProjects = allProjects;
   }
@@ -58,13 +67,13 @@
   @Override
   public String apply(ProjectResource rsrc, Input input)
       throws AuthException, ResourceConflictException, ResourceNotFoundException,
-          UnprocessableEntityException, IOException {
+          UnprocessableEntityException, IOException, PermissionBackendException {
     return apply(rsrc, input, true);
   }
 
   public String apply(ProjectResource rsrc, Input input, boolean checkIfAdmin)
       throws AuthException, ResourceConflictException, ResourceNotFoundException,
-          UnprocessableEntityException, IOException {
+          UnprocessableEntityException, IOException, PermissionBackendException {
     ProjectControl ctl = rsrc.getControl();
     String parentName =
         MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
@@ -97,10 +106,11 @@
   }
 
   public void validateParentUpdate(final ProjectControl ctl, String newParent, boolean checkIfAdmin)
-      throws AuthException, ResourceConflictException, UnprocessableEntityException {
+      throws AuthException, ResourceConflictException, UnprocessableEntityException,
+          PermissionBackendException {
     IdentifiedUser user = ctl.getUser().asIdentifiedUser();
-    if (checkIfAdmin && !user.getCapabilities().canAdministrateServer()) {
-      throw new AuthException("not administrator");
+    if (checkIfAdmin) {
+      permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
     }
 
     if (ctl.getProject().getNameKey().equals(allProjects)) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
index 9971b0c..dbf5b10 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.DynamicOptions;
@@ -29,6 +30,9 @@
 import com.google.gerrit.server.git.ProjectRunnable;
 import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.git.WorkQueue.CancelableRunnable;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.sshd.SshScope.Context;
@@ -83,6 +87,7 @@
 
   @Inject @CommandExecutor private WorkQueue.Executor executor;
 
+  @Inject private PermissionBackend permissionBackend;
   @Inject private CurrentUser user;
 
   @Inject private SshScope.Context context;
@@ -251,7 +256,7 @@
   protected void startThread(final CommandRunnable thunk) {
     final TaskThunk tt = new TaskThunk(thunk);
 
-    if (isAdminHighPriorityCommand() && user.getCapabilities().canAdministrateServer()) {
+    if (isAdminHighPriorityCommand()) {
       // Admin commands should not block the main work threads (there
       // might be an interactive shell there), nor should they wait
       // for the main work threads.
@@ -263,7 +268,15 @@
   }
 
   private boolean isAdminHighPriorityCommand() {
-    return getClass().getAnnotation(AdminHighPriorityCommand.class) != null;
+    if (getClass().getAnnotation(AdminHighPriorityCommand.class) != null) {
+      try {
+        permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+        return true;
+      } catch (AuthException | PermissionBackendException e) {
+        return false;
+      }
+    }
+    return false;
   }
 
   /**
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index bfa1a81..8c6db83 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -229,7 +229,8 @@
   }
 
   private void addSshKeys(List<String> sshKeys)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException {
+      throws RestApiException, OrmException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     for (final String sshKey : sshKeys) {
       AddSshKey.Input in = new AddSshKey.Input();
       in.raw = RawInputUtil.create(sshKey.getBytes(UTF_8), "plain/text");
@@ -258,7 +259,7 @@
 
   private void deleteSshKey(SshKeyInfo i)
       throws AuthException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException {
+          ConfigInvalidException, PermissionBackendException {
     AccountSshKey sshKey =
         new AccountSshKey(new AccountSshKey.Id(user.getAccountId(), i.seq), i.sshPublicKey);
     deleteSshKey.apply(new AccountResource.SshKey(user, sshKey), null);