Merge changes from topic 'permission-backend'
* changes: Convert SuggestChangeReviewers to use PermissionBackend Reduce visibility of some RefControl methods Convert update and forceUpdate to PermissionBackend Convert ListProjects to PermissionBackend Convert reading project access to use PermissionBackend Convert ListDashboards to PermissionBackend Convert parsing projects to use PermissionBackend Convert ListTasks and TaskCollection to PermissionBackend Convert ListChildProjects to PermissionBackend Convert GitOverHttp and Gitweb to PermissionBackend Convert SuggestParentCandidates to PermissionBackend Convert administrateServer to PermissionBackend Check delete change with PermissionBackend
This commit is contained in:
@@ -30,6 +30,9 @@ import com.google.gerrit.server.CurrentUser;
|
||||
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 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||
}
|
||||
|
||||
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 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||
@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 @@ public class AddSshKey implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ import com.google.gerrit.server.permissions.GlobalOrPluginPermission;
|
||||
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 @@ public class CapabilityControl {
|
||||
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 @@ public class CapabilityControl {
|
||||
|
||||
/** @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 @@ public class CapabilityControl {
|
||||
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 @@ public class CapabilityControl {
|
||||
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 @@ public class CapabilityControl {
|
||||
case VIEW_QUEUE:
|
||||
return canPerform(perm.permissionName())
|
||||
|| canPerform(GlobalCapability.MAINTAIN_SERVER)
|
||||
|| canAdministrateServer();
|
||||
|| isAdmin_DoNotUse();
|
||||
|
||||
case CREATE_ACCOUNT:
|
||||
case CREATE_GROUP:
|
||||
@@ -251,7 +269,7 @@ public class CapabilityControl {
|
||||
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:
|
||||
|
||||
@@ -19,6 +19,9 @@ import com.google.gerrit.extensions.restapi.Response;
|
||||
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 class DeleteSshKey implements RestModifyView<AccountResource.SshKey, Inpu
|
||||
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 @@ public class DeleteSshKey implements RestModifyView<AccountResource.SshKey, Inpu
|
||||
@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());
|
||||
|
||||
@@ -25,6 +25,9 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
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 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
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 @@ public class DeleteWatchedProjects
|
||||
@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();
|
||||
|
||||
@@ -17,12 +17,16 @@ package com.google.gerrit.server.account;
|
||||
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 @@ public class Emails
|
||||
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 @@ public class Emails
|
||||
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 @@ public class Emails
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ import com.google.gerrit.server.CurrentUser;
|
||||
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 @@ public class GetDiffPreferences implements RestReadView<AccountResource> {
|
||||
|
||||
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();
|
||||
|
||||
@@ -23,6 +23,9 @@ import com.google.gerrit.reviewdb.client.Account;
|
||||
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 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
|
||||
@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 :
|
||||
|
||||
@@ -127,7 +127,7 @@ public class GroupControl {
|
||||
return user.isInternalUser()
|
||||
|| groupBackend.isVisibleToAll(group.getGroupUUID())
|
||||
|| user.getEffectiveGroups().contains(group.getGroupUUID())
|
||||
|| user.getCapabilities().canAdministrateServer()
|
||||
|| user.getCapabilities().isAdmin_DoNotUse()
|
||||
|| isOwner();
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class GroupControl {
|
||||
AccountGroup.UUID ownerUUID = accountGroup.getOwnerGroupUUID();
|
||||
isOwner =
|
||||
getUser().getEffectiveGroups().contains(ownerUUID)
|
||||
|| getUser().getCapabilities().canAdministrateServer();
|
||||
|| getUser().getCapabilities().isAdmin_DoNotUse();
|
||||
}
|
||||
return isOwner;
|
||||
}
|
||||
|
||||
@@ -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.reviewdb.client.Account;
|
||||
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 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
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 @@ public class PostWatchedProjects
|
||||
@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 @@ public class PostWatchedProjects
|
||||
|
||||
@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);
|
||||
@@ -73,7 +80,8 @@ public class PostWatchedProjects
|
||||
}
|
||||
|
||||
private Map<ProjectWatchKey, Set<NotifyType>> asMap(List<ProjectWatchInfo> input)
|
||||
throws BadRequestException, UnprocessableEntityException, IOException {
|
||||
throws BadRequestException, UnprocessableEntityException, IOException,
|
||||
PermissionBackendException {
|
||||
Map<ProjectWatchKey, Set<NotifyType>> m = new HashMap<>();
|
||||
for (ProjectWatchInfo info : input) {
|
||||
if (info.project == null) {
|
||||
|
||||
@@ -29,6 +29,9 @@ import com.google.gerrit.server.account.PutHttpPassword.Input;
|
||||
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 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
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 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
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 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
@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 @@ public class PutHttpPassword implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
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);
|
||||
|
||||
@@ -25,6 +25,9 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
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 @@ public class PutUsername implements RestModifyView<AccountResource, Input> {
|
||||
|
||||
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 @@ public class PutUsername implements RestModifyView<AccountResource, Input> {
|
||||
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 @@ public class PutUsername implements RestModifyView<AccountResource, Input> {
|
||||
@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)) {
|
||||
|
||||
@@ -255,7 +255,7 @@ public class AccountApiImpl implements AccountApi {
|
||||
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 class AccountApiImpl implements AccountApi {
|
||||
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 @@ public class AccountApiImpl implements AccountApi {
|
||||
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 class AccountApiImpl implements AccountApi {
|
||||
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 @@ public class AccountApiImpl implements AccountApi {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ class ChangeApiImpl implements ChangeApi {
|
||||
public void delete() throws RestApiException {
|
||||
try {
|
||||
deleteChange.apply(change, null);
|
||||
} catch (UpdateException e) {
|
||||
} catch (UpdateException | PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot delete change", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.gerrit.extensions.restapi.Url;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.change.ChangesCollection;
|
||||
import com.google.gerrit.server.change.CreateChange;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.query.change.QueryChanges;
|
||||
import com.google.gerrit.server.update.UpdateException;
|
||||
@@ -87,7 +88,11 @@ class ChangesImpl implements Changes {
|
||||
try {
|
||||
ChangeInfo out = createChange.apply(TopLevelResource.INSTANCE, in).value();
|
||||
return api.create(changes.parse(new Change.Id(out._number)));
|
||||
} catch (OrmException | IOException | InvalidChangeOperationException | UpdateException e) {
|
||||
} catch (OrmException
|
||||
| IOException
|
||||
| InvalidChangeOperationException
|
||||
| UpdateException
|
||||
| PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot create change", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ class RevisionApiImpl implements RevisionApi {
|
||||
public void delete() throws RestApiException {
|
||||
try {
|
||||
deleteDraft.apply(revision, null);
|
||||
} catch (UpdateException e) {
|
||||
} catch (UpdateException | OrmException | PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot delete draft ps", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ class GroupsImpl implements Groups {
|
||||
for (String project : req.getProjects()) {
|
||||
try {
|
||||
list.addProject(projects.parse(tlr, IdString.fromDecoded(project)).getControl());
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | PermissionBackendException e) {
|
||||
throw new RestApiException("Error looking up project " + project, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -378,14 +378,18 @@ public class ProjectApiImpl implements ProjectApi {
|
||||
public List<ProjectInfo> children(boolean recursive) throws RestApiException {
|
||||
ListChildProjects list = children.list();
|
||||
list.setRecursive(recursive);
|
||||
return list.apply(checkExists());
|
||||
try {
|
||||
return list.apply(checkExists());
|
||||
} catch (PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot list children", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChildProjectApi child(String name) throws RestApiException {
|
||||
try {
|
||||
return childApi.create(children.parse(checkExists(), IdString.fromDecoded(name)));
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot parse child project", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.ListProjects;
|
||||
import com.google.gerrit.server.project.ListProjects.FilterType;
|
||||
import com.google.gerrit.server.project.ProjectsCollection;
|
||||
@@ -52,8 +53,8 @@ class ProjectsImpl implements Projects {
|
||||
return api.create(projects.parse(name));
|
||||
} catch (UnprocessableEntityException e) {
|
||||
return api.create(name);
|
||||
} catch (IOException e) {
|
||||
throw new RestApiException("Cannot retrieve project");
|
||||
} catch (IOException | PermissionBackendException e) {
|
||||
throw new RestApiException("Cannot retrieve project", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +78,17 @@ class ProjectsImpl implements Projects {
|
||||
return new ListRequest() {
|
||||
@Override
|
||||
public SortedMap<String, ProjectInfo> getAsMap() throws RestApiException {
|
||||
return list(this);
|
||||
try {
|
||||
return list(this);
|
||||
} catch (PermissionBackendException e) {
|
||||
throw new RestApiException("project list unavailable", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private SortedMap<String, ProjectInfo> list(ListRequest request) throws RestApiException {
|
||||
private SortedMap<String, ProjectInfo> list(ListRequest request)
|
||||
throws RestApiException, PermissionBackendException {
|
||||
ListProjects lp = listProvider.get();
|
||||
lp.setShowDescription(request.getDescription());
|
||||
lp.setLimit(request.getLimit());
|
||||
|
||||
@@ -15,8 +15,12 @@
|
||||
package com.google.gerrit.server.args4j;
|
||||
|
||||
import com.google.gerrit.common.ProjectUtil;
|
||||
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.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.inject.Inject;
|
||||
@@ -34,18 +38,22 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ProjectControlHandler extends OptionHandler<ProjectControl> {
|
||||
private static final Logger log = LoggerFactory.getLogger(ProjectControlHandler.class);
|
||||
|
||||
private final ProjectControl.GenericFactory projectControlFactory;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
|
||||
@Inject
|
||||
public ProjectControlHandler(
|
||||
final ProjectControl.GenericFactory projectControlFactory,
|
||||
ProjectControl.GenericFactory projectControlFactory,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
@Assisted final CmdLineParser parser,
|
||||
@Assisted final OptionDef option,
|
||||
@Assisted final Setter<ProjectControl> setter) {
|
||||
super(parser, option, setter);
|
||||
this.projectControlFactory = projectControlFactory;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@@ -69,14 +77,15 @@ public class ProjectControlHandler extends OptionHandler<ProjectControl> {
|
||||
String nameWithoutSuffix = ProjectUtil.stripGitSuffix(projectName);
|
||||
Project.NameKey nameKey = new Project.NameKey(nameWithoutSuffix);
|
||||
|
||||
final ProjectControl control;
|
||||
ProjectControl control;
|
||||
try {
|
||||
control =
|
||||
projectControlFactory.validateFor(
|
||||
nameKey, ProjectControl.OWNER | ProjectControl.VISIBLE, user.get());
|
||||
control = projectControlFactory.controlFor(nameKey, user.get());
|
||||
permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
|
||||
} catch (AuthException e) {
|
||||
throw new CmdLineException(owner, new NoSuchProjectException(nameKey).getMessage());
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new CmdLineException(owner, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
} catch (PermissionBackendException | IOException e) {
|
||||
log.warn("Cannot load project " + nameWithoutSuffix, e);
|
||||
throw new CmdLineException(owner, new NoSuchProjectException(nameKey).getMessage());
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.google.gerrit.server.config.AnonymousCowardName;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeUtil;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
@@ -145,7 +146,7 @@ public class CreateChange implements RestModifyView<TopLevelResource, ChangeInpu
|
||||
@Override
|
||||
public Response<ChangeInfo> apply(TopLevelResource parent, ChangeInput input)
|
||||
throws OrmException, IOException, InvalidChangeOperationException, RestApiException,
|
||||
UpdateException {
|
||||
UpdateException, PermissionBackendException {
|
||||
if (Strings.isNullOrEmpty(input.project)) {
|
||||
throw new BadRequestException("project must be non-empty");
|
||||
}
|
||||
|
||||
@@ -15,20 +15,24 @@
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.Response;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.RestModifyView;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.change.DeleteChange.Input;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.permissions.ChangePermission;
|
||||
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.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.Order;
|
||||
import com.google.gerrit.server.update.UpdateException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -42,6 +46,8 @@ public class DeleteChange
|
||||
private final Provider<ReviewDb> db;
|
||||
private final BatchUpdate.Factory updateFactory;
|
||||
private final Provider<DeleteChangeOp> opProvider;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final boolean allowDrafts;
|
||||
|
||||
@Inject
|
||||
@@ -49,16 +55,33 @@ public class DeleteChange
|
||||
Provider<ReviewDb> db,
|
||||
BatchUpdate.Factory updateFactory,
|
||||
Provider<DeleteChangeOp> opProvider,
|
||||
Provider<CurrentUser> user,
|
||||
PermissionBackend permissionBackend,
|
||||
@GerritServerConfig Config cfg) {
|
||||
this.db = db;
|
||||
this.updateFactory = updateFactory;
|
||||
this.opProvider = opProvider;
|
||||
this.user = user;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.allowDrafts = DeleteChangeOp.allowDrafts(cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<?> apply(ChangeResource rsrc, Input input)
|
||||
throws RestApiException, UpdateException {
|
||||
throws RestApiException, UpdateException, PermissionBackendException {
|
||||
if (rsrc.getChange().getStatus() == Change.Status.MERGED) {
|
||||
throw new MethodNotAllowedException("delete not permitted");
|
||||
} else if (!allowDrafts && rsrc.getChange().getStatus() == Change.Status.DRAFT) {
|
||||
// If drafts are disabled, only an administrator can delete a draft.
|
||||
try {
|
||||
permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||
} catch (AuthException e) {
|
||||
throw new MethodNotAllowedException("Draft workflow is disabled");
|
||||
}
|
||||
} else {
|
||||
rsrc.permissions().database(db).check(ChangePermission.DELETE);
|
||||
}
|
||||
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
|
||||
Change.Id id = rsrc.getChange().getId();
|
||||
@@ -71,21 +94,33 @@ public class DeleteChange
|
||||
|
||||
@Override
|
||||
public UiAction.Description getDescription(ChangeResource rsrc) {
|
||||
try {
|
||||
Change.Status status = rsrc.getChange().getStatus();
|
||||
ChangeControl changeControl = rsrc.getControl();
|
||||
boolean visible =
|
||||
isActionAllowed(changeControl, status) && changeControl.canDelete(db.get(), status);
|
||||
return new UiAction.Description()
|
||||
.setLabel("Delete")
|
||||
.setTitle("Delete change " + rsrc.getId())
|
||||
.setVisible(visible);
|
||||
} catch (OrmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
Change.Status status = rsrc.getChange().getStatus();
|
||||
PermissionBackend.ForChange perm = rsrc.permissions().database(db);
|
||||
return new UiAction.Description()
|
||||
.setLabel("Delete")
|
||||
.setTitle("Delete change " + rsrc.getId())
|
||||
.setVisible(couldDeleteWhenIn(status) && perm.testOrFalse(ChangePermission.DELETE));
|
||||
}
|
||||
|
||||
private boolean isActionAllowed(ChangeControl changeControl, Status status) {
|
||||
return status != Status.DRAFT || allowDrafts || changeControl.isAdmin();
|
||||
private boolean couldDeleteWhenIn(Change.Status status) {
|
||||
switch (status) {
|
||||
case NEW:
|
||||
case ABANDONED:
|
||||
// New or abandoned changes can be deleted with the right permissions.
|
||||
return true;
|
||||
|
||||
case MERGED:
|
||||
// Merged changes should never be deleted.
|
||||
return false;
|
||||
|
||||
case DRAFT:
|
||||
if (allowDrafts) {
|
||||
// Drafts can only be deleted if the server has drafts enabled.
|
||||
return true;
|
||||
}
|
||||
// If drafts are disabled, only administrators may delete.
|
||||
return permissionBackend.user(user).testOrFalse(GlobalPermission.ADMINISTRATE_SERVER);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.google.gerrit.server.change;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
@@ -27,7 +26,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDbUtil;
|
||||
import com.google.gerrit.server.PatchSetUtil;
|
||||
import com.google.gerrit.server.StarredChangesUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.update.BatchUpdateOp;
|
||||
import com.google.gerrit.server.update.BatchUpdateReviewDb;
|
||||
@@ -62,7 +60,6 @@ class DeleteChangeOp implements BatchUpdateOp {
|
||||
private final PatchSetUtil psUtil;
|
||||
private final StarredChangesUtil starredChangesUtil;
|
||||
private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore;
|
||||
private final boolean allowDrafts;
|
||||
|
||||
private Change.Id id;
|
||||
|
||||
@@ -70,12 +67,10 @@ class DeleteChangeOp implements BatchUpdateOp {
|
||||
DeleteChangeOp(
|
||||
PatchSetUtil psUtil,
|
||||
StarredChangesUtil starredChangesUtil,
|
||||
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore,
|
||||
@GerritServerConfig Config cfg) {
|
||||
DynamicItem<AccountPatchReviewStore> accountPatchReviewStore) {
|
||||
this.psUtil = psUtil;
|
||||
this.starredChangesUtil = starredChangesUtil;
|
||||
this.accountPatchReviewStore = accountPatchReviewStore;
|
||||
this.allowDrafts = allowDrafts(cfg);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,8 +94,7 @@ class DeleteChangeOp implements BatchUpdateOp {
|
||||
}
|
||||
|
||||
private void ensureDeletable(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets)
|
||||
throws ResourceConflictException, MethodNotAllowedException, OrmException, AuthException,
|
||||
IOException {
|
||||
throws ResourceConflictException, MethodNotAllowedException, IOException {
|
||||
Change.Status status = ctx.getChange().getStatus();
|
||||
if (status == Change.Status.MERGED) {
|
||||
throw new MethodNotAllowedException("Deleting merged change " + id + " is not allowed");
|
||||
@@ -114,14 +108,7 @@ class DeleteChangeOp implements BatchUpdateOp {
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.getControl().canDelete(ctx.getDb(), status)) {
|
||||
throw new AuthException("Deleting change " + id + " is not permitted");
|
||||
}
|
||||
|
||||
if (status == Change.Status.DRAFT) {
|
||||
if (!allowDrafts && !ctx.getControl().isAdmin()) {
|
||||
throw new MethodNotAllowedException("Draft workflow is disabled");
|
||||
}
|
||||
for (PatchSet ps : patchSets) {
|
||||
if (!ps.isDraft()) {
|
||||
throw new ResourceConflictException(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
@@ -32,6 +33,8 @@ import com.google.gerrit.server.change.DeleteDraftPatchSet.Input;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
|
||||
import com.google.gerrit.server.permissions.ChangePermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.BatchUpdateOp;
|
||||
@@ -81,7 +84,12 @@ public class DeleteDraftPatchSet
|
||||
|
||||
@Override
|
||||
public Response<?> apply(RevisionResource rsrc, Input input)
|
||||
throws RestApiException, UpdateException {
|
||||
throws RestApiException, UpdateException, OrmException, PermissionBackendException {
|
||||
if (isDeletingOnlyPatchSet(rsrc)) {
|
||||
// A change cannot have zero patch sets; the change is deleted instead.
|
||||
rsrc.permissions().database(db).check(ChangePermission.DELETE);
|
||||
}
|
||||
|
||||
try (BatchUpdate bu =
|
||||
updateFactory.create(db.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) {
|
||||
bu.setOrder(Order.DB_BEFORE_REPO);
|
||||
@@ -91,6 +99,12 @@ public class DeleteDraftPatchSet
|
||||
return Response.none();
|
||||
}
|
||||
|
||||
private boolean isDeletingOnlyPatchSet(RevisionResource rsrc) throws OrmException {
|
||||
Collection<PatchSet> patchSets = psUtil.byChange(db.get(), rsrc.getNotes());
|
||||
return patchSets.size() == 1
|
||||
&& Iterables.getOnlyElement(patchSets).getId().equals(rsrc.getPatchSet().getId());
|
||||
}
|
||||
|
||||
private class Op implements BatchUpdateOp {
|
||||
private final PatchSet.Id psId;
|
||||
|
||||
@@ -183,15 +197,14 @@ public class DeleteDraftPatchSet
|
||||
@Override
|
||||
public UiAction.Description getDescription(RevisionResource rsrc) {
|
||||
try {
|
||||
int psCount = psUtil.byChange(db.get(), rsrc.getNotes()).size();
|
||||
return new UiAction.Description()
|
||||
.setLabel("Delete")
|
||||
.setTitle(String.format("Delete draft revision %d", rsrc.getPatchSet().getPatchSetId()))
|
||||
.setVisible(
|
||||
allowDrafts
|
||||
&& rsrc.getPatchSet().isDraft()
|
||||
&& rsrc.getControl().canDelete(db.get(), Change.Status.DRAFT)
|
||||
&& psCount > 1);
|
||||
&& psUtil.byChange(db.get(), rsrc.getNotes()).size() > 1
|
||||
&& rsrc.getControl().canDelete(db.get(), Change.Status.DRAFT));
|
||||
} catch (OrmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import com.google.gerrit.server.ReviewersUtil;
|
||||
import com.google.gerrit.server.ReviewersUtil.VisibilityControl;
|
||||
import com.google.gerrit.server.account.AccountVisibility;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -45,6 +47,7 @@ public class SuggestChangeReviewers extends SuggestReviewers
|
||||
)
|
||||
boolean excludeGroups;
|
||||
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> self;
|
||||
|
||||
@Inject
|
||||
@@ -52,10 +55,12 @@ public class SuggestChangeReviewers extends SuggestReviewers
|
||||
AccountVisibility av,
|
||||
GenericFactory identifiedUserFactory,
|
||||
Provider<ReviewDb> dbProvider,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> self,
|
||||
@GerritServerConfig Config cfg,
|
||||
ReviewersUtil reviewersUtil) {
|
||||
super(av, identifiedUserFactory, dbProvider, cfg, reviewersUtil);
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
@@ -73,7 +78,7 @@ public class SuggestChangeReviewers extends SuggestReviewers
|
||||
excludeGroups);
|
||||
}
|
||||
|
||||
private VisibilityControl getVisibility(final ChangeResource rsrc) {
|
||||
private VisibilityControl getVisibility(ChangeResource rsrc) {
|
||||
if (rsrc.getControl().getRefControl().isVisibleByRegisteredUsers()) {
|
||||
return new VisibilityControl() {
|
||||
@Override
|
||||
@@ -82,13 +87,15 @@ public class SuggestChangeReviewers extends SuggestReviewers
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Use the destination reference, not the change, as drafts may deny
|
||||
// anyone who is not already a reviewer.
|
||||
PermissionBackend.ForRef perm = permissionBackend.user(self).ref(rsrc.getChange().getDest());
|
||||
return new VisibilityControl() {
|
||||
@Override
|
||||
public boolean isVisibleTo(Account.Id account) throws OrmException {
|
||||
IdentifiedUser who = identifiedUserFactory.create(account);
|
||||
// we can't use changeControl directly as it won't suggest reviewers
|
||||
// to drafts
|
||||
return rsrc.getControl().forUser(who).isRefVisible();
|
||||
return perm.user(who).testOrFalse(RefPermission.READ);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ import com.google.gerrit.server.git.WorkQueue.Task;
|
||||
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.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.util.IdGenerator;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -45,18 +44,13 @@ import java.util.concurrent.TimeUnit;
|
||||
public class ListTasks implements RestReadView<ConfigResource> {
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final WorkQueue workQueue;
|
||||
private final ProjectCache projectCache;
|
||||
private final Provider<CurrentUser> self;
|
||||
|
||||
@Inject
|
||||
public ListTasks(
|
||||
PermissionBackend permissionBackend,
|
||||
WorkQueue workQueue,
|
||||
ProjectCache projectCache,
|
||||
Provider<CurrentUser> self) {
|
||||
PermissionBackend permissionBackend, WorkQueue workQueue, Provider<CurrentUser> self) {
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.workQueue = workQueue;
|
||||
this.projectCache = projectCache;
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
@@ -82,8 +76,15 @@ public class ListTasks implements RestReadView<ConfigResource> {
|
||||
if (task.projectName != null) {
|
||||
Boolean visible = visibilityCache.get(task.projectName);
|
||||
if (visible == null) {
|
||||
ProjectState e = projectCache.get(new Project.NameKey(task.projectName));
|
||||
visible = e != null ? e.controlFor(user).isVisible() : false;
|
||||
try {
|
||||
permissionBackend
|
||||
.user(user)
|
||||
.project(new Project.NameKey(task.projectName))
|
||||
.check(ProjectPermission.ACCESS);
|
||||
visible = true;
|
||||
} catch (AuthException e) {
|
||||
visible = false;
|
||||
}
|
||||
visibilityCache.put(task.projectName, visible);
|
||||
}
|
||||
if (visible) {
|
||||
|
||||
@@ -27,8 +27,7 @@ import com.google.gerrit.server.git.WorkQueue.Task;
|
||||
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.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -40,7 +39,6 @@ public class TasksCollection implements ChildCollection<ConfigResource, TaskReso
|
||||
private final WorkQueue workQueue;
|
||||
private final Provider<CurrentUser> self;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final ProjectCache projectCache;
|
||||
|
||||
@Inject
|
||||
TasksCollection(
|
||||
@@ -48,14 +46,12 @@ public class TasksCollection implements ChildCollection<ConfigResource, TaskReso
|
||||
ListTasks list,
|
||||
WorkQueue workQueue,
|
||||
Provider<CurrentUser> self,
|
||||
PermissionBackend permissionBackend,
|
||||
ProjectCache projectCache) {
|
||||
PermissionBackend permissionBackend) {
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
this.workQueue = workQueue;
|
||||
this.self = self;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.projectCache = projectCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,22 +75,27 @@ public class TasksCollection implements ChildCollection<ConfigResource, TaskReso
|
||||
}
|
||||
|
||||
Task<?> task = workQueue.getTask(taskId);
|
||||
if (task instanceof ProjectTask) {
|
||||
try {
|
||||
permissionBackend
|
||||
.user(user)
|
||||
.project(((ProjectTask<?>) task).getProjectNameKey())
|
||||
.check(ProjectPermission.ACCESS);
|
||||
return new TaskResource(task);
|
||||
} catch (AuthException e) {
|
||||
// Fall through and try view queue permission.
|
||||
}
|
||||
}
|
||||
|
||||
if (task != null) {
|
||||
try {
|
||||
permissionBackend.user(user).check(GlobalPermission.VIEW_QUEUE);
|
||||
return new TaskResource(task);
|
||||
} catch (AuthException e) {
|
||||
// Fall through and try filtering.
|
||||
}
|
||||
|
||||
if (task instanceof ProjectTask) {
|
||||
ProjectTask<?> projectTask = ((ProjectTask<?>) task);
|
||||
ProjectState e = projectCache.get(projectTask.getProjectNameKey());
|
||||
if (e != null && e.controlFor(user).isVisible()) {
|
||||
return new TaskResource(task);
|
||||
}
|
||||
// Fall through and return not found.
|
||||
}
|
||||
}
|
||||
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,11 @@ import com.google.gerrit.server.mail.MailUtil.MailRecipients;
|
||||
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.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
@@ -292,6 +297,8 @@ public class ReceiveCommits {
|
||||
private final Provider<InternalChangeQuery> queryProvider;
|
||||
private final ChangeNotes.Factory notesFactory;
|
||||
private final AccountResolver accountResolver;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final PermissionBackend.ForProject permissions;
|
||||
private final CmdLineParser.Factory optionParserFactory;
|
||||
private final GitReferenceUpdated gitRefUpdated;
|
||||
private final PatchSetInfoFactory patchSetInfoFactory;
|
||||
@@ -357,6 +364,7 @@ public class ReceiveCommits {
|
||||
Provider<InternalChangeQuery> queryProvider,
|
||||
ChangeNotes.Factory notesFactory,
|
||||
AccountResolver accountResolver,
|
||||
PermissionBackend permissionBackend,
|
||||
CmdLineParser.Factory optionParserFactory,
|
||||
GitReferenceUpdated gitRefUpdated,
|
||||
PatchSetInfoFactory patchSetInfoFactory,
|
||||
@@ -389,13 +397,14 @@ public class ReceiveCommits {
|
||||
SetHashtagsOp.Factory hashtagsFactory,
|
||||
ReplaceOp.Factory replaceOpFactory,
|
||||
MergedByPushOp.Factory mergedByPushOpFactory)
|
||||
throws IOException {
|
||||
throws IOException, PermissionBackendException {
|
||||
this.user = projectControl.getUser().asIdentifiedUser();
|
||||
this.db = db;
|
||||
this.seq = seq;
|
||||
this.queryProvider = queryProvider;
|
||||
this.notesFactory = notesFactory;
|
||||
this.accountResolver = accountResolver;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.optionParserFactory = optionParserFactory;
|
||||
this.gitRefUpdated = gitRefUpdated;
|
||||
this.patchSetInfoFactory = patchSetInfoFactory;
|
||||
@@ -463,9 +472,15 @@ public class ReceiveCommits {
|
||||
}
|
||||
});
|
||||
|
||||
if (!projectControl.allRefsAreVisible()) {
|
||||
permissions = permissionBackend.user(user).project(project.getNameKey());
|
||||
// If the user lacks READ permission, some references may be filtered and hidden from view.
|
||||
// Check objects mentioned inside the incoming pack file are reachable from visible refs.
|
||||
try {
|
||||
permissions.check(ProjectPermission.READ);
|
||||
} catch (AuthException e) {
|
||||
rp.setCheckReferencedObjectsAreReachable(receiveConfig.checkReferencedObjectsAreReachable);
|
||||
}
|
||||
|
||||
rp.setAdvertiseRefsHook(
|
||||
new VisibleRefFilter(tagCache, notesFactory, changeCache, repo, projectControl, db, false));
|
||||
List<AdvertiseRefsHook> advHooks = new ArrayList<>(3);
|
||||
@@ -579,7 +594,16 @@ public class ReceiveCommits {
|
||||
batch.setRefLogIdent(rp.getRefLogIdent());
|
||||
batch.setRefLogMessage("push", true);
|
||||
|
||||
parseCommands(commands);
|
||||
try {
|
||||
parseCommands(commands);
|
||||
} catch (PermissionBackendException err) {
|
||||
for (ReceiveCommand cmd : batch.getCommands()) {
|
||||
if (cmd.getResult() == NOT_ATTEMPTED) {
|
||||
cmd.setResult(REJECTED_OTHER_REASON, "internal server error");
|
||||
}
|
||||
}
|
||||
logError(String.format("Failed to process refs in %s", project.getName()), err);
|
||||
}
|
||||
if (magicBranch != null && magicBranch.cmd.getResult() == NOT_ATTEMPTED) {
|
||||
selectNewAndReplacedChangesFromMagicBranch();
|
||||
}
|
||||
@@ -923,7 +947,8 @@ public class ReceiveCommits {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
private void parseCommands(Collection<ReceiveCommand> commands) {
|
||||
private void parseCommands(Collection<ReceiveCommand> commands)
|
||||
throws PermissionBackendException {
|
||||
List<String> optionList = rp.getPushOptions();
|
||||
if (optionList != null) {
|
||||
for (String option : optionList) {
|
||||
@@ -1042,10 +1067,13 @@ public class ReceiveCommits {
|
||||
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) {
|
||||
@@ -1154,24 +1182,29 @@ public class ReceiveCommits {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseUpdate(ReceiveCommand cmd) {
|
||||
private void parseUpdate(ReceiveCommand cmd) throws PermissionBackendException {
|
||||
logDebug("Updating {}", cmd);
|
||||
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
|
||||
if (ctl.canUpdate()) {
|
||||
boolean ok;
|
||||
try {
|
||||
permissions.ref(cmd.getRefName()).check(RefPermission.UPDATE);
|
||||
ok = true;
|
||||
} catch (AuthException err) {
|
||||
ok = false;
|
||||
}
|
||||
if (ok) {
|
||||
if (isHead(cmd) && !isCommit(cmd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validRefOperation(cmd)) {
|
||||
return;
|
||||
}
|
||||
validateNewCommits(ctl, cmd);
|
||||
validateNewCommits(projectControl.controlForRef(cmd.getRefName()), cmd);
|
||||
batch.addCommand(cmd);
|
||||
} else {
|
||||
if (RefNames.REFS_CONFIG.equals(ctl.getRefName())) {
|
||||
if (RefNames.REFS_CONFIG.equals(cmd.getRefName())) {
|
||||
errors.put(Error.CONFIG_UPDATE, RefNames.REFS_CONFIG);
|
||||
} else {
|
||||
errors.put(Error.UPDATE, ctl.getRefName());
|
||||
errors.put(Error.UPDATE, cmd.getRefName());
|
||||
}
|
||||
reject(cmd, "prohibited by Gerrit: ref update access denied");
|
||||
}
|
||||
@@ -1215,7 +1248,7 @@ public class ReceiveCommits {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseRewind(ReceiveCommand cmd) {
|
||||
private void parseRewind(ReceiveCommand cmd) throws PermissionBackendException {
|
||||
RevCommit newObject;
|
||||
try {
|
||||
newObject = rp.getRevWalk().parseCommit(cmd.getNewId());
|
||||
@@ -1238,7 +1271,14 @@ public class ReceiveCommits {
|
||||
}
|
||||
}
|
||||
|
||||
if (ctl.canForceUpdate()) {
|
||||
boolean ok;
|
||||
try {
|
||||
permissions.ref(cmd.getRefName()).check(RefPermission.FORCE_UPDATE);
|
||||
ok = true;
|
||||
} catch (AuthException err) {
|
||||
ok = false;
|
||||
}
|
||||
if (ok) {
|
||||
if (!validRefOperation(cmd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
|
||||
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.PluginConfig;
|
||||
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.io.IOException;
|
||||
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 @@ public class MergeValidators {
|
||||
|
||||
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 class MergeValidators {
|
||||
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 @@ public class MergeValidators {
|
||||
}
|
||||
} 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) {
|
||||
|
||||
@@ -356,7 +356,6 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
||||
|
||||
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 @@ public class ListGroups implements RestReadView<TopLevelResource> {
|
||||
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;
|
||||
|
||||
@@ -17,9 +17,13 @@ package com.google.gerrit.server.mail.send;
|
||||
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 @@ public class AddKeySender extends OutgoingEmail {
|
||||
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 @@ public class AddKeySender extends OutgoingEmail {
|
||||
@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 @@ public class AddKeySender extends OutgoingEmail {
|
||||
@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 @@ public class AddKeySender extends OutgoingEmail {
|
||||
|
||||
@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
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.permissions;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.common.data.LabelType;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
@@ -85,7 +86,7 @@ public abstract class PermissionBackend {
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -183,6 +184,30 @@ public abstract class PermissionBackend {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a set of projects using {@code check(perm)}.
|
||||
*
|
||||
* @param perm required permission in a project to be included in result.
|
||||
* @param projects candidate set of projects; may be empty.
|
||||
* @return filtered set of {@code projects} where {@code check(perm)} was successful.
|
||||
* @throws PermissionBackendException backend cannot access its internal state.
|
||||
*/
|
||||
public Set<Project.NameKey> filter(ProjectPermission perm, Collection<Project.NameKey> projects)
|
||||
throws PermissionBackendException {
|
||||
checkNotNull(perm, "ProjectPermission");
|
||||
checkNotNull(projects, "projects");
|
||||
Set<Project.NameKey> allowed = Sets.newHashSetWithExpectedSize(projects.size());
|
||||
for (Project.NameKey project : projects) {
|
||||
try {
|
||||
project(project).check(perm);
|
||||
allowed.add(project);
|
||||
} catch (AuthException e) {
|
||||
// Do not include this project in allowed.
|
||||
}
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/** PermissionBackend scoped to a user and project. */
|
||||
@@ -204,6 +229,15 @@ public abstract class PermissionBackend {
|
||||
public boolean test(ProjectPermission perm) throws PermissionBackendException {
|
||||
return test(EnumSet.of(perm)).contains(perm);
|
||||
}
|
||||
|
||||
public boolean testOrFalse(ProjectPermission perm) {
|
||||
try {
|
||||
return test(perm);
|
||||
} catch (PermissionBackendException e) {
|
||||
logger.warn("Cannot test " + perm + "; assuming false", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** PermissionBackend scoped to a user, project and reference. */
|
||||
@@ -227,6 +261,25 @@ public abstract class PermissionBackend {
|
||||
public boolean test(RefPermission perm) throws PermissionBackendException {
|
||||
return test(EnumSet.of(perm)).contains(perm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if user may be able to perform the permission.
|
||||
*
|
||||
* <p>Similar to {@link #test(RefPermission)} except this method returns {@code false} instead
|
||||
* of throwing an exception.
|
||||
*
|
||||
* @param perm the permission to test.
|
||||
* @return true if the user might be able to perform the permission; false if the user may be
|
||||
* missing the necessary grants or state, or if the backend threw an exception.
|
||||
*/
|
||||
public boolean testOrFalse(RefPermission perm) {
|
||||
try {
|
||||
return test(perm);
|
||||
} catch (PermissionBackendException e) {
|
||||
logger.warn("Cannot test " + perm + "; assuming false", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** PermissionBackend scoped to a user, project, reference and change. */
|
||||
|
||||
@@ -19,6 +19,19 @@ import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public enum ProjectPermission {
|
||||
/**
|
||||
* Can access at least one reference or change within the repository.
|
||||
*
|
||||
* <p>Checking this permission instead of {@link #READ} may require filtering to hide specific
|
||||
* references or changes, which can be expensive.
|
||||
*/
|
||||
ACCESS,
|
||||
|
||||
/**
|
||||
* Can read all references in the repository.
|
||||
*
|
||||
* <p>This is a stronger form of {@link #ACCESS} where no filtering is required.
|
||||
*/
|
||||
READ(Permission.READ);
|
||||
|
||||
private final String name;
|
||||
|
||||
@@ -251,7 +251,7 @@ public class ChangeControl {
|
||||
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 @@ public class ChangeControl {
|
||||
|
||||
switch (status) {
|
||||
case DRAFT:
|
||||
return (isOwner() || getRefControl().canDeleteDrafts());
|
||||
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 @@ public class ChangeControl {
|
||||
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 @@ public class ChangeControl {
|
||||
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 @@ public class ChangeControl {
|
||||
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 @@ public class ChangeControl {
|
||||
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 @@ public class ChangeControl {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ 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.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -50,7 +51,7 @@ public class ChildProjectsCollection
|
||||
|
||||
@Override
|
||||
public ChildProjectResource parse(ProjectResource parent, IdString id)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
throws ResourceNotFoundException, IOException, PermissionBackendException {
|
||||
ProjectResource p = projectsCollection.parse(TopLevelResource.INSTANCE, id);
|
||||
for (ProjectState pp : p.getControl().getProjectState().parents()) {
|
||||
if (parent.getNameKey().equals(pp.getProject().getNameKey())) {
|
||||
|
||||
@@ -55,6 +55,7 @@ import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.git.RepositoryCaseMismatchException;
|
||||
import com.google.gerrit.server.group.GroupsCollection;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
|
||||
import com.google.gerrit.server.validators.ValidationException;
|
||||
import com.google.inject.Inject;
|
||||
@@ -148,7 +149,8 @@ public class CreateProject implements RestModifyView<TopLevelResource, ProjectIn
|
||||
@Override
|
||||
public Response<ProjectInfo> apply(TopLevelResource resource, ProjectInput input)
|
||||
throws BadRequestException, UnprocessableEntityException, ResourceConflictException,
|
||||
ResourceNotFoundException, IOException, ConfigInvalidException {
|
||||
ResourceNotFoundException, IOException, ConfigInvalidException,
|
||||
PermissionBackendException {
|
||||
if (input == null) {
|
||||
input = new ProjectInput();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
||||
public ProjectAccessInfo apply(Project.NameKey nameKey)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
try {
|
||||
return this.apply(new ProjectResource(projectControlFactory.controlFor(nameKey, self.get())));
|
||||
return apply(new ProjectResource(projectControlFactory.controlFor(nameKey, self.get())));
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(nameKey.get());
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
||||
Project.NameKey projectName = rsrc.getNameKey();
|
||||
ProjectAccessInfo info = new ProjectAccessInfo();
|
||||
ProjectConfig config;
|
||||
ProjectControl pc = open(projectName);
|
||||
ProjectControl pc = createProjectControl(projectName);
|
||||
RefControl metaConfigControl = pc.controlForRef(RefNames.REFS_CONFIG);
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
|
||||
config = ProjectConfig.read(md);
|
||||
@@ -120,11 +120,11 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
||||
md.setMessage("Update group names\n");
|
||||
config.commit(md);
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
pc = createProjectControl(projectName);
|
||||
} else if (config.getRevision() != null
|
||||
&& !config.getRevision().equals(pc.getProjectState().getConfig().getRevision())) {
|
||||
projectCache.evict(config.getProject());
|
||||
pc = open(projectName);
|
||||
pc = createProjectControl(projectName);
|
||||
}
|
||||
} catch (ConfigInvalidException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
@@ -252,11 +252,10 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
||||
return accessSectionInfo;
|
||||
}
|
||||
|
||||
private ProjectControl open(Project.NameKey projectName)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
private ProjectControl createProjectControl(Project.NameKey projectName)
|
||||
throws IOException, ResourceNotFoundException {
|
||||
try {
|
||||
return projectControlFactory.validateFor(
|
||||
projectName, ProjectControl.OWNER | ProjectControl.VISIBLE, self.get());
|
||||
return projectControlFactory.controlFor(projectName, self.get());
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(projectName.get());
|
||||
}
|
||||
|
||||
@@ -14,14 +14,21 @@
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -33,20 +40,23 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
|
||||
private boolean recursive;
|
||||
|
||||
private final ProjectCache projectCache;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final AllProjectsName allProjects;
|
||||
private final ProjectJson json;
|
||||
private final ProjectNode.Factory projectNodeFactory;
|
||||
|
||||
@Inject
|
||||
ListChildProjects(
|
||||
ProjectCache projectCache,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
AllProjectsName allProjectsName,
|
||||
ProjectJson json,
|
||||
ProjectNode.Factory projectNodeFactory) {
|
||||
ProjectJson json) {
|
||||
this.projectCache = projectCache;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.allProjects = allProjectsName;
|
||||
this.json = json;
|
||||
this.projectNodeFactory = projectNodeFactory;
|
||||
}
|
||||
|
||||
public void setRecursive(boolean recursive) {
|
||||
@@ -54,60 +64,82 @@ public class ListChildProjects implements RestReadView<ProjectResource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectInfo> apply(ProjectResource rsrc) {
|
||||
public List<ProjectInfo> apply(ProjectResource rsrc) throws PermissionBackendException {
|
||||
if (recursive) {
|
||||
return getChildProjectsRecursively(rsrc.getNameKey(), rsrc.getControl().getUser());
|
||||
return recursiveChildProjects(rsrc.getNameKey());
|
||||
}
|
||||
return getDirectChildProjects(rsrc.getNameKey());
|
||||
return directChildProjects(rsrc.getNameKey());
|
||||
}
|
||||
|
||||
private List<ProjectInfo> getDirectChildProjects(Project.NameKey parent) {
|
||||
List<ProjectInfo> childProjects = new ArrayList<>();
|
||||
for (Project.NameKey projectName : projectCache.all()) {
|
||||
ProjectState e = projectCache.get(projectName);
|
||||
if (e == null) {
|
||||
// If we can't get it from the cache, pretend it's not present.
|
||||
continue;
|
||||
}
|
||||
if (parent.equals(e.getProject().getParent(allProjects))) {
|
||||
childProjects.add(json.format(e.getProject()));
|
||||
}
|
||||
}
|
||||
return childProjects;
|
||||
}
|
||||
|
||||
private List<ProjectInfo> getChildProjectsRecursively(Project.NameKey parent, CurrentUser user) {
|
||||
Map<Project.NameKey, ProjectNode> projects = new HashMap<>();
|
||||
private List<ProjectInfo> directChildProjects(Project.NameKey parent)
|
||||
throws PermissionBackendException {
|
||||
Map<Project.NameKey, Project> children = new HashMap<>();
|
||||
for (Project.NameKey name : projectCache.all()) {
|
||||
ProjectState p = projectCache.get(name);
|
||||
if (p == null) {
|
||||
// If we can't get it from the cache, pretend it's not present.
|
||||
continue;
|
||||
}
|
||||
projects.put(name, projectNodeFactory.create(p.getProject(), p.controlFor(user).isVisible()));
|
||||
}
|
||||
for (ProjectNode key : projects.values()) {
|
||||
ProjectNode node = projects.get(key.getParentName());
|
||||
if (node != null) {
|
||||
node.addChild(key);
|
||||
ProjectState c = projectCache.get(name);
|
||||
if (c != null && parent.equals(c.getProject().getParent(allProjects))) {
|
||||
children.put(c.getProject().getNameKey(), c.getProject());
|
||||
}
|
||||
}
|
||||
|
||||
ProjectNode n = projects.get(parent);
|
||||
if (n != null) {
|
||||
return getChildProjectsRecursively(n);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return permissionBackend
|
||||
.user(user)
|
||||
.filter(ProjectPermission.ACCESS, children.keySet())
|
||||
.stream()
|
||||
.sorted()
|
||||
.map((p) -> json.format(children.get(p)))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private List<ProjectInfo> getChildProjectsRecursively(ProjectNode p) {
|
||||
List<ProjectInfo> allChildren = new ArrayList<>();
|
||||
for (ProjectNode c : p.getChildren()) {
|
||||
if (c.isVisible()) {
|
||||
allChildren.add(json.format(c.getProject()));
|
||||
allChildren.addAll(getChildProjectsRecursively(c));
|
||||
private List<ProjectInfo> recursiveChildProjects(Project.NameKey parent)
|
||||
throws PermissionBackendException {
|
||||
Map<Project.NameKey, Project> projects = readAllProjects();
|
||||
Multimap<Project.NameKey, Project.NameKey> children = parentToChildren(projects);
|
||||
PermissionBackend.WithUser perm = permissionBackend.user(user);
|
||||
|
||||
List<ProjectInfo> results = new ArrayList<>();
|
||||
depthFirstFormat(results, perm, projects, children, parent);
|
||||
return results;
|
||||
}
|
||||
|
||||
private Map<Project.NameKey, Project> readAllProjects() {
|
||||
Map<Project.NameKey, Project> projects = new HashMap<>();
|
||||
for (Project.NameKey name : projectCache.all()) {
|
||||
ProjectState c = projectCache.get(name);
|
||||
if (c != null) {
|
||||
projects.put(c.getProject().getNameKey(), c.getProject());
|
||||
}
|
||||
}
|
||||
return allChildren;
|
||||
return projects;
|
||||
}
|
||||
|
||||
/** Map of parent project to direct child. */
|
||||
private Multimap<Project.NameKey, Project.NameKey> parentToChildren(
|
||||
Map<Project.NameKey, Project> projects) {
|
||||
Multimap<Project.NameKey, Project.NameKey> m = ArrayListMultimap.create();
|
||||
for (Map.Entry<Project.NameKey, Project> e : projects.entrySet()) {
|
||||
if (!allProjects.equals(e.getKey())) {
|
||||
m.put(e.getValue().getParent(allProjects), e.getKey());
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private void depthFirstFormat(
|
||||
List<ProjectInfo> results,
|
||||
PermissionBackend.WithUser perm,
|
||||
Map<Project.NameKey, Project> projects,
|
||||
Multimap<Project.NameKey, Project.NameKey> children,
|
||||
Project.NameKey parent)
|
||||
throws PermissionBackendException {
|
||||
List<Project.NameKey> canSee =
|
||||
perm.filter(ProjectPermission.ACCESS, children.get(parent))
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(toList());
|
||||
children.removeAll(parent); // removing all entries prevents cycles.
|
||||
|
||||
for (Project.NameKey c : canSee) {
|
||||
results.add(json.format(projects.get(c)));
|
||||
depthFirstFormat(results, perm, projects, children, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,20 @@ import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.project.DashboardsCollection.DashboardInfo;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.BlobBasedConfig;
|
||||
@@ -41,42 +49,56 @@ class ListDashboards implements RestReadView<ProjectResource> {
|
||||
private static final Logger log = LoggerFactory.getLogger(ListDashboards.class);
|
||||
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
|
||||
@Option(name = "--inherited", usage = "include inherited dashboards")
|
||||
private boolean inherited;
|
||||
|
||||
@Inject
|
||||
ListDashboards(GitRepositoryManager gitManager) {
|
||||
ListDashboards(
|
||||
GitRepositoryManager gitManager,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user) {
|
||||
this.gitManager = gitManager;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<?> apply(ProjectResource resource) throws ResourceNotFoundException, IOException {
|
||||
ProjectControl ctl = resource.getControl();
|
||||
String project = ctl.getProject().getName();
|
||||
public List<?> apply(ProjectResource rsrc)
|
||||
throws ResourceNotFoundException, IOException, PermissionBackendException {
|
||||
String project = rsrc.getName();
|
||||
if (!inherited) {
|
||||
return scan(resource.getControl(), project, true);
|
||||
return scan(rsrc.getControl(), project, true);
|
||||
}
|
||||
|
||||
List<List<DashboardInfo>> all = new ArrayList<>();
|
||||
boolean setDefault = true;
|
||||
for (ProjectState ps : ctl.getProjectState().tree()) {
|
||||
ctl = ps.controlFor(ctl.getUser());
|
||||
if (ctl.isVisible()) {
|
||||
List<DashboardInfo> list = scan(ctl, project, setDefault);
|
||||
for (DashboardInfo d : list) {
|
||||
if (d.isDefault != null && Boolean.TRUE.equals(d.isDefault)) {
|
||||
setDefault = false;
|
||||
}
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
all.add(list);
|
||||
for (ProjectState ps : tree(rsrc)) {
|
||||
List<DashboardInfo> list = scan(ps.controlFor(user.get()), project, setDefault);
|
||||
for (DashboardInfo d : list) {
|
||||
if (d.isDefault != null && Boolean.TRUE.equals(d.isDefault)) {
|
||||
setDefault = false;
|
||||
}
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
all.add(list);
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
private Collection<ProjectState> tree(ProjectResource rsrc) throws PermissionBackendException {
|
||||
Map<Project.NameKey, ProjectState> tree = new LinkedHashMap<>();
|
||||
for (ProjectState ps : rsrc.getProjectState().tree()) {
|
||||
tree.put(ps.getProject().getNameKey(), ps);
|
||||
}
|
||||
tree.keySet()
|
||||
.retainAll(permissionBackend.user(user).filter(ProjectPermission.ACCESS, tree.keySet()));
|
||||
return tree.values();
|
||||
}
|
||||
|
||||
private List<DashboardInfo> scan(ProjectControl ctl, String project, boolean setDefault)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
Project.NameKey projectName = ctl.getProject().getNameKey();
|
||||
|
||||
@@ -14,15 +14,20 @@
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import static com.google.gerrit.extensions.client.ProjectState.HIDDEN;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.data.GroupReference;
|
||||
import com.google.gerrit.common.errors.NoSuchGroupException;
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.extensions.common.WebLinkInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.BinaryResult;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
@@ -38,6 +43,9 @@ import com.google.gerrit.server.WebLinks;
|
||||
import com.google.gerrit.server.account.GroupControl;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.group.GroupsCollection;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.util.RegexListSearcher;
|
||||
import com.google.gerrit.server.util.TreeFormatter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
@@ -50,6 +58,8 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -79,12 +89,22 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
boolean matches(Repository git) throws IOException {
|
||||
return !PERMISSIONS.matches(git);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean useMatch() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
PARENT_CANDIDATES {
|
||||
@Override
|
||||
boolean matches(Repository git) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean useMatch() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
PERMISSIONS {
|
||||
@Override
|
||||
@@ -94,15 +114,27 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
&& head.isSymbolic()
|
||||
&& RefNames.REFS_CONFIG.equals(head.getLeaf().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean useMatch() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ALL {
|
||||
@Override
|
||||
boolean matches(Repository git) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean useMatch() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
abstract boolean matches(Repository git) throws IOException;
|
||||
|
||||
abstract boolean useMatch();
|
||||
}
|
||||
|
||||
private final CurrentUser currentUser;
|
||||
@@ -110,6 +142,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
private final GroupsCollection groupsCollection;
|
||||
private final GroupControl.Factory groupControlFactory;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final ProjectNode.Factory projectNodeFactory;
|
||||
private final WebLinks webLinks;
|
||||
|
||||
@@ -229,6 +262,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
GroupsCollection groupsCollection,
|
||||
GroupControl.Factory groupControlFactory,
|
||||
GitRepositoryManager repoManager,
|
||||
PermissionBackend permissionBackend,
|
||||
ProjectNode.Factory projectNodeFactory,
|
||||
WebLinks webLinks) {
|
||||
this.currentUser = currentUser;
|
||||
@@ -236,6 +270,7 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
this.groupsCollection = groupsCollection;
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.repoManager = repoManager;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.projectNodeFactory = projectNodeFactory;
|
||||
this.webLinks = webLinks;
|
||||
}
|
||||
@@ -262,7 +297,8 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(TopLevelResource resource) throws BadRequestException {
|
||||
public Object apply(TopLevelResource resource)
|
||||
throws BadRequestException, PermissionBackendException {
|
||||
if (format == OutputFormat.TEXT) {
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
display(buf);
|
||||
@@ -273,141 +309,123 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
return apply();
|
||||
}
|
||||
|
||||
public SortedMap<String, ProjectInfo> apply() throws BadRequestException {
|
||||
public SortedMap<String, ProjectInfo> apply()
|
||||
throws BadRequestException, PermissionBackendException {
|
||||
format = OutputFormat.JSON;
|
||||
return display(null);
|
||||
}
|
||||
|
||||
public SortedMap<String, ProjectInfo> display(OutputStream displayOutputStream)
|
||||
throws BadRequestException {
|
||||
public SortedMap<String, ProjectInfo> display(@Nullable OutputStream displayOutputStream)
|
||||
throws BadRequestException, PermissionBackendException {
|
||||
if (groupUuid != null) {
|
||||
try {
|
||||
if (!groupControlFactory.controlFor(groupUuid).isVisible()) {
|
||||
return Collections.emptySortedMap();
|
||||
}
|
||||
} catch (NoSuchGroupException ex) {
|
||||
return Collections.emptySortedMap();
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter stdout = null;
|
||||
if (displayOutputStream != null) {
|
||||
stdout =
|
||||
new PrintWriter(new BufferedWriter(new OutputStreamWriter(displayOutputStream, UTF_8)));
|
||||
}
|
||||
|
||||
if (type == FilterType.PARENT_CANDIDATES) {
|
||||
// Historically, PARENT_CANDIDATES implied showDescription.
|
||||
showDescription = true;
|
||||
}
|
||||
|
||||
int foundIndex = 0;
|
||||
int found = 0;
|
||||
TreeMap<String, ProjectInfo> output = new TreeMap<>();
|
||||
Map<String, String> hiddenNames = new HashMap<>();
|
||||
Set<String> rejected = new HashSet<>();
|
||||
|
||||
Map<Project.NameKey, Boolean> accessibleParents = new HashMap<>();
|
||||
PermissionBackend.WithUser perm = permissionBackend.user(currentUser);
|
||||
final TreeMap<Project.NameKey, ProjectNode> treeMap = new TreeMap<>();
|
||||
try {
|
||||
for (final Project.NameKey projectName : scan()) {
|
||||
for (final Project.NameKey projectName : filter(perm)) {
|
||||
final ProjectState e = projectCache.get(projectName);
|
||||
if (e == null) {
|
||||
if (e == null || (!all && e.getProject().getState() == HIDDEN)) {
|
||||
// If we can't get it from the cache, pretend its not present.
|
||||
//
|
||||
// If all wasn't selected, and its HIDDEN, pretend its not present.
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProjectControl pctl = e.controlFor(currentUser);
|
||||
if (groupUuid != null) {
|
||||
try {
|
||||
if (!groupControlFactory.controlFor(groupUuid).isVisible()) {
|
||||
break;
|
||||
}
|
||||
} catch (NoSuchGroupException ex) {
|
||||
break;
|
||||
}
|
||||
if (!pctl.getLocalGroups()
|
||||
.contains(GroupReference.forGroup(groupsCollection.parseId(groupUuid.get())))) {
|
||||
continue;
|
||||
}
|
||||
if (groupUuid != null
|
||||
&& !pctl.getLocalGroups()
|
||||
.contains(GroupReference.forGroup(groupsCollection.parseId(groupUuid.get())))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProjectInfo info = new ProjectInfo();
|
||||
if (type == FilterType.PARENT_CANDIDATES) {
|
||||
ProjectState parentState = Iterables.getFirst(e.parents(), null);
|
||||
if (parentState != null
|
||||
&& !output.keySet().contains(parentState.getProject().getName())
|
||||
&& !rejected.contains(parentState.getProject().getName())) {
|
||||
ProjectControl parentCtrl = parentState.controlFor(currentUser);
|
||||
if (parentCtrl.isVisible() || parentCtrl.isOwner()) {
|
||||
info.name = parentState.getProject().getName();
|
||||
info.description = Strings.emptyToNull(parentState.getProject().getDescription());
|
||||
info.state = parentState.getProject().getState();
|
||||
if (showTree && !format.isJson()) {
|
||||
treeMap.put(projectName, projectNodeFactory.create(pctl.getProject(), true));
|
||||
continue;
|
||||
}
|
||||
|
||||
info.name = projectName.get();
|
||||
if (showTree && format.isJson()) {
|
||||
ProjectState parent = Iterables.getFirst(e.parents(), null);
|
||||
if (parent != null) {
|
||||
if (isParentAccessible(accessibleParents, perm, parent)) {
|
||||
info.parent = parent.getProject().getName();
|
||||
} else {
|
||||
rejected.add(parentState.getProject().getName());
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
final boolean isVisible = pctl.isVisible() || (all && pctl.isOwner());
|
||||
if (showTree && !format.isJson()) {
|
||||
treeMap.put(projectName, projectNodeFactory.create(pctl.getProject(), isVisible));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isVisible && !(showTree && pctl.isOwner())) {
|
||||
// Require the project itself to be visible to the user.
|
||||
//
|
||||
continue;
|
||||
}
|
||||
|
||||
info.name = projectName.get();
|
||||
if (showTree && format.isJson()) {
|
||||
ProjectState parent = Iterables.getFirst(e.parents(), null);
|
||||
if (parent != null) {
|
||||
ProjectControl parentCtrl = parent.controlFor(currentUser);
|
||||
if (parentCtrl.isVisible() || parentCtrl.isOwner()) {
|
||||
info.parent = parent.getProject().getName();
|
||||
} else {
|
||||
info.parent = hiddenNames.get(parent.getProject().getName());
|
||||
if (info.parent == null) {
|
||||
info.parent = "?-" + (hiddenNames.size() + 1);
|
||||
hiddenNames.put(parent.getProject().getName(), info.parent);
|
||||
}
|
||||
info.parent = hiddenNames.get(parent.getProject().getName());
|
||||
if (info.parent == null) {
|
||||
info.parent = "?-" + (hiddenNames.size() + 1);
|
||||
hiddenNames.put(parent.getProject().getName(), info.parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showDescription) {
|
||||
info.description = Strings.emptyToNull(e.getProject().getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
info.state = e.getProject().getState();
|
||||
if (showDescription) {
|
||||
info.description = Strings.emptyToNull(e.getProject().getDescription());
|
||||
}
|
||||
info.state = e.getProject().getState();
|
||||
|
||||
try {
|
||||
if (!showBranch.isEmpty()) {
|
||||
try (Repository git = repoManager.openRepository(projectName)) {
|
||||
if (!type.matches(git)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (!showBranch.isEmpty()) {
|
||||
try (Repository git = repoManager.openRepository(projectName)) {
|
||||
if (!type.matches(git)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Ref> refs = getBranchRefs(projectName, pctl);
|
||||
if (!hasValidRef(refs)) {
|
||||
continue;
|
||||
}
|
||||
List<Ref> refs = getBranchRefs(projectName, pctl);
|
||||
if (!hasValidRef(refs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < showBranch.size(); i++) {
|
||||
Ref ref = refs.get(i);
|
||||
if (ref != null && ref.getObjectId() != null) {
|
||||
if (info.branches == null) {
|
||||
info.branches = new LinkedHashMap<>();
|
||||
}
|
||||
info.branches.put(showBranch.get(i), ref.getObjectId().name());
|
||||
for (int i = 0; i < showBranch.size(); i++) {
|
||||
Ref ref = refs.get(i);
|
||||
if (ref != null && ref.getObjectId() != null) {
|
||||
if (info.branches == null) {
|
||||
info.branches = new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!showTree && type != FilterType.ALL) {
|
||||
try (Repository git = repoManager.openRepository(projectName)) {
|
||||
if (!type.matches(git)) {
|
||||
continue;
|
||||
info.branches.put(showBranch.get(i), ref.getObjectId().name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (RepositoryNotFoundException err) {
|
||||
// If the Git repository is gone, the project doesn't actually exist anymore.
|
||||
continue;
|
||||
} catch (IOException err) {
|
||||
log.warn("Unexpected error reading " + projectName, err);
|
||||
continue;
|
||||
} else if (!showTree && type.useMatch()) {
|
||||
try (Repository git = repoManager.openRepository(projectName)) {
|
||||
if (!type.matches(git)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RepositoryNotFoundException err) {
|
||||
// If the Git repository is gone, the project doesn't actually exist anymore.
|
||||
continue;
|
||||
} catch (IOException err) {
|
||||
log.warn("Unexpected error reading " + projectName, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type != FilterType.PARENT_CANDIDATES) {
|
||||
List<WebLinkInfo> links = webLinks.getProjectLinks(projectName.get());
|
||||
info.webLinks = links.isEmpty() ? null : links;
|
||||
}
|
||||
@@ -415,7 +433,6 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
if (foundIndex++ < start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (limit > 0 && ++found > limit) {
|
||||
break;
|
||||
}
|
||||
@@ -467,6 +484,46 @@ public class ListProjects implements RestReadView<TopLevelResource> {
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<Project.NameKey> filter(PermissionBackend.WithUser perm)
|
||||
throws BadRequestException, PermissionBackendException {
|
||||
Collection<Project.NameKey> matches = Lists.newArrayList(scan());
|
||||
if (type == FilterType.PARENT_CANDIDATES) {
|
||||
matches = parentsOf(matches);
|
||||
}
|
||||
return perm.filter(ProjectPermission.ACCESS, matches).stream().sorted().collect(toList());
|
||||
}
|
||||
|
||||
private Collection<Project.NameKey> parentsOf(Collection<Project.NameKey> matches) {
|
||||
Set<Project.NameKey> parents = new HashSet<>();
|
||||
for (Project.NameKey p : matches) {
|
||||
ProjectState ps = projectCache.get(p);
|
||||
if (ps != null) {
|
||||
Project.NameKey parent = ps.getProject().getParent();
|
||||
if (parent != null) {
|
||||
parents.add(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
private boolean isParentAccessible(
|
||||
Map<Project.NameKey, Boolean> checked, PermissionBackend.WithUser perm, ProjectState p)
|
||||
throws PermissionBackendException {
|
||||
Project.NameKey name = p.getProject().getNameKey();
|
||||
Boolean b = checked.get(name);
|
||||
if (b == null) {
|
||||
try {
|
||||
perm.project(name).check(ProjectPermission.ACCESS);
|
||||
b = true;
|
||||
} catch (AuthException denied) {
|
||||
b = false;
|
||||
}
|
||||
checked.put(name, b);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private Iterable<Project.NameKey> scan() throws BadRequestException {
|
||||
if (matchPrefix != null) {
|
||||
checkMatchOptions(matchSubstring == null && matchRegex == null);
|
||||
|
||||
@@ -324,7 +324,7 @@ public class ProjectControl {
|
||||
/** 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 @@ public class ProjectControl {
|
||||
|
||||
/** 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 */
|
||||
@@ -608,8 +608,11 @@ public class ProjectControl {
|
||||
|
||||
private boolean can(ProjectPermission perm) throws PermissionBackendException {
|
||||
switch (perm) {
|
||||
case ACCESS:
|
||||
return (!isHidden() && isReadable()) || isOwner();
|
||||
|
||||
case READ:
|
||||
return isReadable();
|
||||
return (!isHidden() && allRefsAreVisible()) || isOwner();
|
||||
}
|
||||
throw new PermissionBackendException(perm + " unsupported");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import com.google.gerrit.extensions.client.ProjectState;
|
||||
import com.google.gerrit.extensions.restapi.RestResource;
|
||||
import com.google.gerrit.extensions.restapi.RestView;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
@@ -42,8 +41,8 @@ public class ProjectResource implements RestResource {
|
||||
return control.getProject().getNameKey();
|
||||
}
|
||||
|
||||
public ProjectState getState() {
|
||||
return control.getProject().getState();
|
||||
public ProjectState getProjectState() {
|
||||
return control.getProjectState();
|
||||
}
|
||||
|
||||
public ProjectControl getControl() {
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import com.google.gerrit.common.Nullable;
|
||||
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.IdString;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestCollection;
|
||||
@@ -25,6 +27,9 @@ import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.OutputFormat;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -37,6 +42,7 @@ public class ProjectsCollection
|
||||
private final DynamicMap<RestView<ProjectResource>> views;
|
||||
private final Provider<ListProjects> list;
|
||||
private final ProjectControl.GenericFactory controlFactory;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final CreateProject.Factory createProjectFactory;
|
||||
|
||||
@@ -45,11 +51,13 @@ public class ProjectsCollection
|
||||
DynamicMap<RestView<ProjectResource>> views,
|
||||
Provider<ListProjects> list,
|
||||
ProjectControl.GenericFactory controlFactory,
|
||||
PermissionBackend permissionBackend,
|
||||
CreateProject.Factory factory,
|
||||
Provider<CurrentUser> user) {
|
||||
this.views = views;
|
||||
this.list = list;
|
||||
this.controlFactory = controlFactory;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.createProjectFactory = factory;
|
||||
}
|
||||
@@ -61,7 +69,7 @@ public class ProjectsCollection
|
||||
|
||||
@Override
|
||||
public ProjectResource parse(TopLevelResource parent, IdString id)
|
||||
throws ResourceNotFoundException, IOException {
|
||||
throws ResourceNotFoundException, IOException, PermissionBackendException {
|
||||
ProjectResource rsrc = _parse(id.get(), true);
|
||||
if (rsrc == null) {
|
||||
throw new ResourceNotFoundException(id);
|
||||
@@ -77,8 +85,10 @@ public class ProjectsCollection
|
||||
* @throws UnprocessableEntityException thrown if the project ID cannot be resolved or if the
|
||||
* project is not visible to the calling user
|
||||
* @throws IOException thrown when there is an error.
|
||||
* @throws PermissionBackendException
|
||||
*/
|
||||
public ProjectResource parse(String id) throws UnprocessableEntityException, IOException {
|
||||
public ProjectResource parse(String id)
|
||||
throws UnprocessableEntityException, IOException, PermissionBackendException {
|
||||
return parse(id, true);
|
||||
}
|
||||
|
||||
@@ -86,33 +96,43 @@ public class ProjectsCollection
|
||||
* Parses a project ID from a request body and returns the project.
|
||||
*
|
||||
* @param id ID of the project, can be a project name
|
||||
* @param checkVisibility Whether to check or not that project is visible to the calling user
|
||||
* @param checkAccess if true, check the project is accessible by the current user
|
||||
* @return the project
|
||||
* @throws UnprocessableEntityException thrown if the project ID cannot be resolved or if the
|
||||
* project is not visible to the calling user and checkVisibility is true.
|
||||
* @throws IOException thrown when there is an error.
|
||||
* @throws PermissionBackendException
|
||||
*/
|
||||
public ProjectResource parse(String id, boolean checkVisibility)
|
||||
throws UnprocessableEntityException, IOException {
|
||||
ProjectResource rsrc = _parse(id, checkVisibility);
|
||||
public ProjectResource parse(String id, boolean checkAccess)
|
||||
throws UnprocessableEntityException, IOException, PermissionBackendException {
|
||||
ProjectResource rsrc = _parse(id, checkAccess);
|
||||
if (rsrc == null) {
|
||||
throw new UnprocessableEntityException(String.format("Project Not Found: %s", id));
|
||||
}
|
||||
return rsrc;
|
||||
}
|
||||
|
||||
private ProjectResource _parse(String id, boolean checkVisibility) throws IOException {
|
||||
@Nullable
|
||||
private ProjectResource _parse(String id, boolean checkAccess)
|
||||
throws IOException, PermissionBackendException {
|
||||
if (id.endsWith(Constants.DOT_GIT_EXT)) {
|
||||
id = id.substring(0, id.length() - Constants.DOT_GIT_EXT.length());
|
||||
}
|
||||
|
||||
Project.NameKey nameKey = new Project.NameKey(id);
|
||||
ProjectControl ctl;
|
||||
try {
|
||||
ctl = controlFactory.controlFor(new Project.NameKey(id), user.get());
|
||||
ctl = controlFactory.controlFor(nameKey, user.get());
|
||||
} catch (NoSuchProjectException e) {
|
||||
return null;
|
||||
}
|
||||
if (checkVisibility && !ctl.isVisible() && !ctl.isOwner()) {
|
||||
return null;
|
||||
|
||||
if (checkAccess) {
|
||||
try {
|
||||
permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
|
||||
} catch (AuthException e) {
|
||||
return null; // Pretend like not found on access denied.
|
||||
}
|
||||
}
|
||||
return new ProjectResource(ctl);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if this user can add a new patch set to this ref */
|
||||
public boolean canAddPatchSet() {
|
||||
boolean canAddPatchSet() {
|
||||
return projectControl
|
||||
.controlForRef("refs/for/" + getRefName())
|
||||
.canPerform(Permission.ADD_PATCH_SET)
|
||||
@@ -170,7 +170,7 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if this user can rebase changes on this ref */
|
||||
public boolean canRebase() {
|
||||
boolean canRebase() {
|
||||
return canPerform(Permission.REBASE) && canWrite();
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if the user can update the reference as a fast-forward. */
|
||||
public boolean canUpdate() {
|
||||
private boolean canUpdate() {
|
||||
if (RefNames.REFS_CONFIG.equals(refName) && !projectControl.isOwner()) {
|
||||
// Pushing requires being at least project owner, in addition to push.
|
||||
// Pushing configuration changes modifies the access control
|
||||
@@ -200,7 +200,7 @@ public class RefControl {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if the user can rewind (force push) the reference. */
|
||||
public boolean canForceUpdate() {
|
||||
private boolean canForceUpdate() {
|
||||
if (!canWrite()) {
|
||||
return false;
|
||||
}
|
||||
@@ -227,7 +227,7 @@ public class RefControl {
|
||||
case UNKNOWN:
|
||||
case WEB_BROWSER:
|
||||
default:
|
||||
return getUser().getCapabilities().canAdministrateServer()
|
||||
return getUser().getCapabilities().isAdmin_DoNotUse()
|
||||
|| (isOwner() && !isForceBlocked(Permission.PUSH));
|
||||
}
|
||||
}
|
||||
@@ -373,7 +373,7 @@ public class RefControl {
|
||||
case UNKNOWN:
|
||||
case WEB_BROWSER:
|
||||
default:
|
||||
return getUser().getCapabilities().canAdministrateServer()
|
||||
return getUser().getCapabilities().isAdmin_DoNotUse()
|
||||
|| (isOwner() && !isForceBlocked(Permission.PUSH))
|
||||
|| canPushWithForce()
|
||||
|| canPerform(Permission.DELETE);
|
||||
@@ -412,7 +412,7 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if this user can view draft changes. */
|
||||
public boolean canViewDrafts() {
|
||||
boolean canViewDrafts() {
|
||||
return canPerform(Permission.VIEW_DRAFTS);
|
||||
}
|
||||
|
||||
@@ -422,17 +422,17 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if this user can publish draft changes. */
|
||||
public boolean canPublishDrafts() {
|
||||
boolean canPublishDrafts() {
|
||||
return canPerform(Permission.PUBLISH_DRAFTS);
|
||||
}
|
||||
|
||||
/** @return true if this user can delete draft changes. */
|
||||
public boolean canDeleteDrafts() {
|
||||
boolean canDeleteDrafts() {
|
||||
return canPerform(Permission.DELETE_DRAFTS);
|
||||
}
|
||||
|
||||
/** @return true if this user can delete their own changes. */
|
||||
public boolean canDeleteOwnChanges() {
|
||||
boolean canDeleteOwnChanges() {
|
||||
return canPerform(Permission.DELETE_OWN_CHANGES);
|
||||
}
|
||||
|
||||
@@ -451,12 +451,12 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** @return true if this user can force edit topic names. */
|
||||
public boolean canForceEditTopicName() {
|
||||
boolean canForceEditTopicName() {
|
||||
return canForcePerform(Permission.EDIT_TOPIC_NAME);
|
||||
}
|
||||
|
||||
/** All value ranges of any allowed label permission. */
|
||||
public List<PermissionRange> getLabelRanges(boolean isChangeOwner) {
|
||||
List<PermissionRange> getLabelRanges(boolean isChangeOwner) {
|
||||
List<PermissionRange> r = new ArrayList<>();
|
||||
for (Map.Entry<String, List<PermissionRule>> e : relevant.getDeclaredPermissions()) {
|
||||
if (Permission.isLabel(e.getKey())) {
|
||||
@@ -477,12 +477,12 @@ public class RefControl {
|
||||
}
|
||||
|
||||
/** The range of permitted values associated with a label permission. */
|
||||
public PermissionRange getRange(String permission) {
|
||||
PermissionRange getRange(String permission) {
|
||||
return getRange(permission, false);
|
||||
}
|
||||
|
||||
/** The range of permitted values associated with a label permission. */
|
||||
public PermissionRange getRange(String permission, boolean isChangeOwner) {
|
||||
PermissionRange getRange(String permission, boolean isChangeOwner) {
|
||||
if (Permission.hasRange(permission)) {
|
||||
return toRange(permission, access(permission, isChangeOwner));
|
||||
}
|
||||
|
||||
@@ -41,6 +41,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.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 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||
@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 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
|
||||
@Inject
|
||||
private SetAccess(
|
||||
GroupBackend groupBackend,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
|
||||
AllProjectsName allProjects,
|
||||
Provider<SetParent> setParent,
|
||||
@@ -73,6 +78,7 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
|
||||
GetAccess getAccess,
|
||||
Provider<IdentifiedUser> identifiedUser) {
|
||||
this.groupBackend = groupBackend;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.metaDataUpdateFactory = metaDataUpdateFactory;
|
||||
this.allProjects = allProjects;
|
||||
this.setParent = setParent;
|
||||
@@ -85,7 +91,7 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
|
||||
@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 @@ public class SetAccess implements RestModifyView<ProjectResource, ProjectAccessI
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ import com.google.gerrit.server.IdentifiedUser;
|
||||
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 @@ public class SetParent implements RestModifyView<ProjectResource, Input> {
|
||||
}
|
||||
|
||||
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 @@ public class SetParent implements RestModifyView<ProjectResource, Input> {
|
||||
@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 class SetParent implements RestModifyView<ProjectResource, Input> {
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -14,65 +14,61 @@
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@Singleton
|
||||
public class SuggestParentCandidates {
|
||||
private final ProjectControl.Factory projectControlFactory;
|
||||
private final ProjectCache projectCache;
|
||||
private final AllProjectsName allProject;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final AllProjectsName allProjects;
|
||||
|
||||
@Inject
|
||||
SuggestParentCandidates(
|
||||
final ProjectControl.Factory projectControlFactory,
|
||||
final ProjectCache projectCache,
|
||||
final AllProjectsName allProject) {
|
||||
this.projectControlFactory = projectControlFactory;
|
||||
ProjectCache projectCache,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
AllProjectsName allProjects) {
|
||||
this.projectCache = projectCache;
|
||||
this.allProject = allProject;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.allProjects = allProjects;
|
||||
}
|
||||
|
||||
public List<Project.NameKey> getNameKeys() throws NoSuchProjectException {
|
||||
List<Project> pList = getProjects();
|
||||
final List<Project.NameKey> nameKeys = new ArrayList<>(pList.size());
|
||||
for (Project p : pList) {
|
||||
nameKeys.add(p.getNameKey());
|
||||
}
|
||||
return nameKeys;
|
||||
public List<Project.NameKey> getNameKeys() throws PermissionBackendException {
|
||||
return permissionBackend
|
||||
.user(user)
|
||||
.filter(ProjectPermission.ACCESS, parents())
|
||||
.stream()
|
||||
.sorted()
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
public List<Project> getProjects() throws NoSuchProjectException {
|
||||
Set<Project> projects =
|
||||
new TreeSet<>(
|
||||
new Comparator<Project>() {
|
||||
@Override
|
||||
public int compare(Project o1, Project o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
private Set<Project.NameKey> parents() {
|
||||
Set<Project.NameKey> parents = new HashSet<>();
|
||||
for (Project.NameKey p : projectCache.all()) {
|
||||
try {
|
||||
final ProjectControl control = projectControlFactory.controlFor(p);
|
||||
final Project.NameKey parentK = control.getProject().getParent();
|
||||
if (parentK != null) {
|
||||
ProjectControl pControl = projectControlFactory.controlFor(parentK);
|
||||
if (pControl.isVisible() || pControl.isOwner()) {
|
||||
projects.add(pControl.getProject());
|
||||
}
|
||||
ProjectState ps = projectCache.get(p);
|
||||
if (ps != null) {
|
||||
Project.NameKey parent = ps.getProject().getParent();
|
||||
if (parent != null) {
|
||||
parents.add(parent);
|
||||
}
|
||||
} catch (NoSuchProjectException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
projects.add(projectControlFactory.controlFor(allProject).getProject());
|
||||
return new ArrayList<>(projects);
|
||||
parents.add(allProjects);
|
||||
return parents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package com.google.gerrit.server.query.change;
|
||||
import com.google.gerrit.extensions.common.ProjectInfo;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.ListChildProjects;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectResource;
|
||||
@@ -27,8 +28,12 @@ import com.google.inject.Provider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ParentProjectPredicate extends OrPredicate<ChangeData> {
|
||||
private static final Logger log = LoggerFactory.getLogger(ParentProjectPredicate.class);
|
||||
|
||||
protected final String value;
|
||||
|
||||
public ParentProjectPredicate(
|
||||
@@ -52,10 +57,15 @@ public class ParentProjectPredicate extends OrPredicate<ChangeData> {
|
||||
|
||||
List<Predicate<ChangeData>> r = new ArrayList<>();
|
||||
r.add(new ProjectPredicate(projectState.getProject().getName()));
|
||||
ListChildProjects children = listChildProjects.get();
|
||||
children.setRecursive(true);
|
||||
for (ProjectInfo p : children.apply(new ProjectResource(projectState.controlFor(self.get())))) {
|
||||
r.add(new ProjectPredicate(p.name));
|
||||
try {
|
||||
ProjectResource proj = new ProjectResource(projectState.controlFor(self.get()));
|
||||
ListChildProjects children = listChildProjects.get();
|
||||
children.setRecursive(true);
|
||||
for (ProjectInfo p : children.apply(proj)) {
|
||||
r.add(new ProjectPredicate(p.name));
|
||||
}
|
||||
} catch (PermissionBackendException e) {
|
||||
log.warn("cannot check permissions to expand child projects", e);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user