Convert RequireCapability checks to PermissionBackend

Replace CapabilityUtils with support in PermissionBackend to check if
the caller has at least one of the specified permissions parsed from
class annotation.

This enables hiding canPerform(String) from CapabilityControl, which
makes it much harder to bypass the PermissionBackend.

Assume anyone with ADMINISTRATE_SERVER also has any PluginPermission.
This is carried over from CapabilityUtils, which skip any further
checks when the user has canAdministrateServer.

Update the error message in GarbageCollectionIT to now be the generic
"maintain server not permitted".

Change-Id: I9458bd55fa1c9709557ae1ad95a57a1d968c52a3
This commit is contained in:
Shawn Pearce
2017-02-20 14:57:11 -08:00
committed by David Pursehouse
parent e9e1af205c
commit 79a899e505
19 changed files with 311 additions and 245 deletions

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.server.api.accounts;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
import com.google.gerrit.extensions.api.accounts.AccountApi;
import com.google.gerrit.extensions.api.accounts.AccountInput;
@@ -32,6 +31,9 @@ import com.google.gerrit.server.account.AccountResource;
import com.google.gerrit.server.account.AccountsCollection;
import com.google.gerrit.server.account.CreateAccount;
import com.google.gerrit.server.account.QueryAccounts;
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;
@@ -44,6 +46,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
public class AccountsImpl implements Accounts {
private final AccountsCollection accounts;
private final AccountApiImpl.Factory api;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> self;
private final CreateAccount.Factory createAccount;
private final Provider<QueryAccounts> queryAccountsProvider;
@@ -52,11 +55,13 @@ public class AccountsImpl implements Accounts {
AccountsImpl(
AccountsCollection accounts,
AccountApiImpl.Factory api,
PermissionBackend permissionBackend,
Provider<CurrentUser> self,
CreateAccount.Factory createAccount,
Provider<QueryAccounts> queryAccountsProvider) {
this.accounts = accounts;
this.api = api;
this.permissionBackend = permissionBackend;
this.self = self;
this.createAccount = createAccount;
this.queryAccountsProvider = queryAccountsProvider;
@@ -96,12 +101,12 @@ public class AccountsImpl implements Accounts {
if (checkNotNull(in, "AccountInput").username == null) {
throw new BadRequestException("AccountInput must specify username");
}
checkRequiresCapability(self, null, CreateAccount.class);
try {
AccountInfo info =
createAccount.create(in.username).apply(TopLevelResource.INSTANCE, in).value();
CreateAccount impl = createAccount.create(in.username);
permissionBackend.user(self).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
AccountInfo info = impl.apply(TopLevelResource.INSTANCE, in).value();
return id(info._accountId);
} catch (OrmException | IOException | ConfigInvalidException e) {
} catch (OrmException | IOException | ConfigInvalidException | PermissionBackendException e) {
throw new RestApiException("Cannot create account " + in.username, e);
}
}

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.server.api.groups;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
import com.google.gerrit.extensions.api.groups.GroupApi;
import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -32,6 +31,9 @@ import com.google.gerrit.server.group.CreateGroup;
import com.google.gerrit.server.group.GroupsCollection;
import com.google.gerrit.server.group.ListGroups;
import com.google.gerrit.server.group.QueryGroups;
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;
@@ -49,6 +51,7 @@ class GroupsImpl implements Groups {
private final Provider<ListGroups> listGroups;
private final Provider<QueryGroups> queryGroups;
private final Provider<CurrentUser> user;
private final PermissionBackend permissionBackend;
private final CreateGroup.Factory createGroup;
private final GroupApiImpl.Factory api;
@@ -60,6 +63,7 @@ class GroupsImpl implements Groups {
Provider<ListGroups> listGroups,
Provider<QueryGroups> queryGroups,
Provider<CurrentUser> user,
PermissionBackend permissionBackend,
CreateGroup.Factory createGroup,
GroupApiImpl.Factory api) {
this.accounts = accounts;
@@ -68,6 +72,7 @@ class GroupsImpl implements Groups {
this.listGroups = listGroups;
this.queryGroups = queryGroups;
this.user = user;
this.permissionBackend = permissionBackend;
this.createGroup = createGroup;
this.api = api;
}
@@ -89,11 +94,12 @@ class GroupsImpl implements Groups {
if (checkNotNull(in, "GroupInput").name == null) {
throw new BadRequestException("GroupInput must specify name");
}
checkRequiresCapability(user, null, CreateGroup.class);
try {
GroupInfo info = createGroup.create(in.name).apply(TopLevelResource.INSTANCE, in);
CreateGroup impl = createGroup.create(in.name);
permissionBackend.user(user).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
GroupInfo info = impl.apply(TopLevelResource.INSTANCE, in);
return id(info.id);
} catch (OrmException | IOException e) {
} catch (OrmException | IOException | PermissionBackendException e) {
throw new RestApiException("Cannot create group " + in.name, e);
}
}

View File

@@ -14,8 +14,6 @@
package com.google.gerrit.server.api.projects;
import static com.google.gerrit.server.account.CapabilityUtils.checkRequiresCapability;
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
import com.google.gerrit.extensions.api.projects.BranchApi;
@@ -39,6 +37,9 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.server.CurrentUser;
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.ChildProjectsCollection;
import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.server.project.CreateProject;
@@ -71,6 +72,7 @@ public class ProjectApiImpl implements ProjectApi {
}
private final CurrentUser user;
private final PermissionBackend permissionBackend;
private final CreateProject.Factory createProjectFactory;
private final ProjectApiImpl.Factory projectApi;
private final ProjectsCollection projects;
@@ -97,6 +99,7 @@ public class ProjectApiImpl implements ProjectApi {
@AssistedInject
ProjectApiImpl(
CurrentUser user,
PermissionBackend permissionBackend,
CreateProject.Factory createProjectFactory,
ProjectApiImpl.Factory projectApi,
ProjectsCollection projects,
@@ -120,6 +123,7 @@ public class ProjectApiImpl implements ProjectApi {
@Assisted ProjectResource project) {
this(
user,
permissionBackend,
createProjectFactory,
projectApi,
projects,
@@ -147,6 +151,7 @@ public class ProjectApiImpl implements ProjectApi {
@AssistedInject
ProjectApiImpl(
CurrentUser user,
PermissionBackend permissionBackend,
CreateProject.Factory createProjectFactory,
ProjectApiImpl.Factory projectApi,
ProjectsCollection projects,
@@ -170,6 +175,7 @@ public class ProjectApiImpl implements ProjectApi {
@Assisted String name) {
this(
user,
permissionBackend,
createProjectFactory,
projectApi,
projects,
@@ -196,6 +202,7 @@ public class ProjectApiImpl implements ProjectApi {
private ProjectApiImpl(
CurrentUser user,
PermissionBackend permissionBackend,
CreateProject.Factory createProjectFactory,
ProjectApiImpl.Factory projectApi,
ProjectsCollection projects,
@@ -219,6 +226,7 @@ public class ProjectApiImpl implements ProjectApi {
CommitApiImpl.Factory commitApi,
String name) {
this.user = user;
this.permissionBackend = permissionBackend;
this.createProjectFactory = createProjectFactory;
this.projectApi = projectApi;
this.projects = projects;
@@ -257,10 +265,11 @@ public class ProjectApiImpl implements ProjectApi {
if (in.name != null && !name.equals(in.name)) {
throw new BadRequestException("name must match input.name");
}
checkRequiresCapability(user, null, CreateProject.class);
createProjectFactory.create(name).apply(TopLevelResource.INSTANCE, in);
CreateProject impl = createProjectFactory.create(name);
permissionBackend.user(user).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
impl.apply(TopLevelResource.INSTANCE, in);
return projectApi.create(projects.parse(name));
} catch (IOException | ConfigInvalidException e) {
} catch (IOException | ConfigInvalidException | PermissionBackendException e) {
throw new RestApiException("Cannot create project: " + e.getMessage(), e);
}
}