Allow administrators to see other user capabilities

Expand /accounts/{id}/capabilities to permit an administrator
to inspect another user's effective capabilities by identifying
the user with anything AccountResolver understands.

This is a stepping stone to making more data available about
other accounts.

Change-Id: I81c786825f6531844b36f6178e5ed7324ccebd3a
This commit is contained in:
Shawn Pearce
2013-01-16 20:09:46 -08:00
parent d18ef139f3
commit 0a8969410f
5 changed files with 58 additions and 4 deletions

View File

@@ -14,35 +14,51 @@
package com.google.gerrit.server.account; package com.google.gerrit.server.account;
import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException; import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestCollection; import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.restapi.TopLevelResource; import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.util.Url;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.util.Set;
public class AccountsCollection implements public class AccountsCollection implements
RestCollection<TopLevelResource, AccountResource> { RestCollection<TopLevelResource, AccountResource> {
private final Provider<CurrentUser> self; private final Provider<CurrentUser> self;
private final AccountResolver resolver;
private final AccountControl.Factory accountControlFactory;
private final IdentifiedUser.GenericFactory userFactory;
private final DynamicMap<RestView<AccountResource>> views; private final DynamicMap<RestView<AccountResource>> views;
@Inject @Inject
AccountsCollection(Provider<CurrentUser> self, AccountsCollection(Provider<CurrentUser> self,
AccountResolver resolver,
AccountControl.Factory accountControlFactory,
IdentifiedUser.GenericFactory userFactory,
DynamicMap<RestView<AccountResource>> views) { DynamicMap<RestView<AccountResource>> views) {
this.self = self; this.self = self;
this.resolver = resolver;
this.accountControlFactory = accountControlFactory;
this.userFactory = userFactory;
this.views = views; this.views = views;
} }
@Override @Override
public AccountResource parse(TopLevelResource root, String id) public AccountResource parse(TopLevelResource root, String id)
throws ResourceNotFoundException, AuthException { throws ResourceNotFoundException, AuthException, OrmException {
CurrentUser user = self.get();
if ("self".equals(id)) { if ("self".equals(id)) {
CurrentUser user = self.get();
if (user instanceof IdentifiedUser) { if (user instanceof IdentifiedUser) {
return new AccountResource((IdentifiedUser) user); return new AccountResource((IdentifiedUser) user);
} else if (user instanceof AnonymousUser) { } else if (user instanceof AnonymousUser) {
@@ -50,6 +66,17 @@ public class AccountsCollection implements
} else { } else {
throw new ResourceNotFoundException(id); throw new ResourceNotFoundException(id);
} }
}
Set<Account.Id> matches = resolver.findAll(Url.decode(id));
if (matches.size() != 1) {
throw new ResourceNotFoundException(id);
}
Account.Id a = Iterables.getOnlyElement(matches);
if (accountControlFactory.get().canSee(a)
|| user.getCapabilities().canAdministrateServer()) {
return new AccountResource(userFactory.create(a));
} else { } else {
throw new ResourceNotFoundException(id); throw new ResourceNotFoundException(id);
} }

View File

@@ -16,22 +16,27 @@ package com.google.gerrit.server.account;
import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountResource.Capability; import com.google.gerrit.server.account.AccountResource.Capability;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
class Capabilities implements class Capabilities implements
ChildCollection<AccountResource, AccountResource.Capability> { ChildCollection<AccountResource, AccountResource.Capability> {
private final Provider<CurrentUser> self;
private final DynamicMap<RestView<AccountResource.Capability>> views; private final DynamicMap<RestView<AccountResource.Capability>> views;
private final Provider<GetCapabilities> get; private final Provider<GetCapabilities> get;
@Inject @Inject
Capabilities( Capabilities(
Provider<CurrentUser> self,
DynamicMap<RestView<AccountResource.Capability>> views, DynamicMap<RestView<AccountResource.Capability>> views,
Provider<GetCapabilities> get) { Provider<GetCapabilities> get) {
this.self = self;
this.views = views; this.views = views;
this.get = get; this.get = get;
} }
@@ -43,7 +48,12 @@ class Capabilities implements
@Override @Override
public Capability parse(AccountResource parent, String id) public Capability parse(AccountResource parent, String id)
throws ResourceNotFoundException, Exception { throws ResourceNotFoundException, AuthException {
if (self.get() != parent.getUser()
&& !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("restricted to administrator");
}
CapabilityControl cap = parent.getUser().getCapabilities(); CapabilityControl cap = parent.getUser().getCapabilities();
if (cap.canPerform(id) if (cap.canPerform(id)
|| (cap.canAdministrateServer() && GlobalCapability.isCapability(id))) { || (cap.canAdministrateServer() && GlobalCapability.isCapability(id))) {

View File

@@ -32,13 +32,17 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GlobalCapability; import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException; import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult; import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.RestReadView; import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OutputFormat; import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.account.AccountResource.Capability; import com.google.gerrit.server.account.AccountResource.Capability;
import com.google.gerrit.server.git.QueueProvider; import com.google.gerrit.server.git.QueueProvider;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.kohsuke.args4j.Option; import org.kohsuke.args4j.Option;
@@ -67,9 +71,21 @@ class GetCapabilities implements RestReadView<AccountResource> {
} }
private Set<String> query; private Set<String> query;
private final Provider<CurrentUser> self;
@Inject
GetCapabilities(Provider<CurrentUser> self) {
this.self = self;
}
@Override @Override
public Object apply(AccountResource resource) public Object apply(AccountResource resource)
throws BadRequestException, Exception { throws BadRequestException, Exception {
if (self.get() != resource.getUser()
&& !self.get().getCapabilities().canAdministrateServer()) {
throw new AuthException("restricted to administrator");
}
CapabilityControl cc = resource.getUser().getCapabilities(); CapabilityControl cc = resource.getUser().getCapabilities();
Map<String, Object> have = Maps.newLinkedHashMap(); Map<String, Object> have = Maps.newLinkedHashMap();
for (String name : GlobalCapability.getAllNames()) { for (String name : GlobalCapability.getAllNames()) {

View File

@@ -34,6 +34,7 @@ import com.google.gerrit.server.InternalUser;
import com.google.gerrit.server.MimeUtilFileTypeRegistry; import com.google.gerrit.server.MimeUtilFileTypeRegistry;
import com.google.gerrit.server.account.AccountByEmailCacheImpl; import com.google.gerrit.server.account.AccountByEmailCacheImpl;
import com.google.gerrit.server.account.AccountCacheImpl; import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountInfoCacheFactory; import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.account.AccountResolver; import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.AccountVisibility; import com.google.gerrit.server.account.AccountVisibility;
@@ -196,6 +197,7 @@ public class GerritGlobalModule extends FactoryModule {
bind(IdentifiedUser.GenericFactory.class).in(SINGLETON); bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
bind(ChangeControl.GenericFactory.class); bind(ChangeControl.GenericFactory.class);
bind(ProjectControl.GenericFactory.class); bind(ProjectControl.GenericFactory.class);
bind(AccountControl.Factory.class);
factory(FunctionState.Factory.class); factory(FunctionState.Factory.class);
install(new AuditModule()); install(new AuditModule());

View File

@@ -60,7 +60,6 @@ public class GerritRequestModule extends FactoryModule {
bind(PerRequestProjectControlCache.class).in(RequestScoped.class); bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
bind(ChangeControl.Factory.class).in(SINGLETON); bind(ChangeControl.Factory.class).in(SINGLETON);
bind(ProjectControl.Factory.class).in(SINGLETON); bind(ProjectControl.Factory.class).in(SINGLETON);
bind(AccountControl.Factory.class).in(SINGLETON);
factory(SubmoduleOp.Factory.class); factory(SubmoduleOp.Factory.class);
factory(MergeOp.Factory.class); factory(MergeOp.Factory.class);