diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java | 503 |
1 files changed, 0 insertions, 503 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java deleted file mode 100644 index 61d6020dee..0000000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright (C) 2013 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.change; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.gerrit.extensions.client.ReviewerState.CC; -import static com.google.gerrit.extensions.client.ReviewerState.REVIEWER; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ListMultimap; -import com.google.common.collect.Lists; -import com.google.gerrit.common.Nullable; -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.common.data.GroupDescription; -import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.extensions.api.changes.AddReviewerInput; -import com.google.gerrit.extensions.api.changes.AddReviewerResult; -import com.google.gerrit.extensions.api.changes.NotifyHandling; -import com.google.gerrit.extensions.api.changes.RecipientType; -import com.google.gerrit.extensions.api.changes.ReviewerInfo; -import com.google.gerrit.extensions.client.ReviewerState; -import com.google.gerrit.extensions.common.AccountInfo; -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.UnprocessableEntityException; -import com.google.gerrit.reviewdb.client.Account; -import com.google.gerrit.reviewdb.client.AccountGroup; -import com.google.gerrit.reviewdb.client.Change; -import com.google.gerrit.reviewdb.client.PatchSetApproval; -import com.google.gerrit.reviewdb.server.ReviewDb; -import com.google.gerrit.server.AnonymousUser; -import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.account.AccountLoader; -import com.google.gerrit.server.account.AccountsCollection; -import com.google.gerrit.server.account.GroupMembers; -import com.google.gerrit.server.config.GerritServerConfig; -import com.google.gerrit.server.group.GroupsCollection; -import com.google.gerrit.server.group.SystemGroupBackend; -import com.google.gerrit.server.mail.Address; -import com.google.gerrit.server.mail.send.OutgoingEmailValidator; -import com.google.gerrit.server.notedb.ChangeNotes; -import com.google.gerrit.server.notedb.NotesMigration; -import com.google.gerrit.server.permissions.ChangePermission; -import com.google.gerrit.server.permissions.PermissionBackend; -import com.google.gerrit.server.permissions.PermissionBackendException; -import com.google.gerrit.server.permissions.RefPermission; -import com.google.gerrit.server.project.NoSuchProjectException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.query.change.ChangeData; -import com.google.gerrit.server.update.BatchUpdate; -import com.google.gerrit.server.update.RetryHelper; -import com.google.gerrit.server.update.RetryingRestModifyView; -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; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; -import org.eclipse.jgit.errors.ConfigInvalidException; -import org.eclipse.jgit.lib.Config; - -@Singleton -public class PostReviewers - extends RetryingRestModifyView<ChangeResource, AddReviewerInput, AddReviewerResult> { - - public static final int DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK = 10; - public static final int DEFAULT_MAX_REVIEWERS = 20; - - private final AccountsCollection accounts; - private final ReviewerResource.Factory reviewerFactory; - private final PermissionBackend permissionBackend; - - private final GroupsCollection groupsCollection; - private final GroupMembers.Factory groupMembersFactory; - private final AccountLoader.Factory accountLoaderFactory; - private final Provider<ReviewDb> dbProvider; - private final ChangeData.Factory changeDataFactory; - private final IdentifiedUser.GenericFactory identifiedUserFactory; - private final Config cfg; - private final ReviewerJson json; - private final NotesMigration migration; - private final NotifyUtil notifyUtil; - private final ProjectCache projectCache; - private final Provider<AnonymousUser> anonymousProvider; - private final PostReviewersOp.Factory postReviewersOpFactory; - private final OutgoingEmailValidator validator; - - @Inject - PostReviewers( - AccountsCollection accounts, - ReviewerResource.Factory reviewerFactory, - PermissionBackend permissionBackend, - GroupsCollection groupsCollection, - GroupMembers.Factory groupMembersFactory, - AccountLoader.Factory accountLoaderFactory, - Provider<ReviewDb> db, - ChangeData.Factory changeDataFactory, - RetryHelper retryHelper, - IdentifiedUser.GenericFactory identifiedUserFactory, - @GerritServerConfig Config cfg, - ReviewerJson json, - NotesMigration migration, - NotifyUtil notifyUtil, - ProjectCache projectCache, - Provider<AnonymousUser> anonymousProvider, - PostReviewersOp.Factory postReviewersOpFactory, - OutgoingEmailValidator validator) { - super(retryHelper); - this.accounts = accounts; - this.reviewerFactory = reviewerFactory; - this.permissionBackend = permissionBackend; - this.groupsCollection = groupsCollection; - this.groupMembersFactory = groupMembersFactory; - this.accountLoaderFactory = accountLoaderFactory; - this.dbProvider = db; - this.changeDataFactory = changeDataFactory; - this.identifiedUserFactory = identifiedUserFactory; - this.cfg = cfg; - this.json = json; - this.migration = migration; - this.notifyUtil = notifyUtil; - this.projectCache = projectCache; - this.anonymousProvider = anonymousProvider; - this.postReviewersOpFactory = postReviewersOpFactory; - this.validator = validator; - } - - @Override - protected AddReviewerResult applyImpl( - BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input) - throws IOException, OrmException, RestApiException, UpdateException, - PermissionBackendException, ConfigInvalidException { - if (input.reviewer == null) { - throw new BadRequestException("missing reviewer field"); - } - - Addition addition = prepareApplication(rsrc, input, true); - if (addition.op == null) { - return addition.result; - } - try (BatchUpdate bu = - updateFactory.create( - dbProvider.get(), rsrc.getProject(), rsrc.getUser(), TimeUtil.nowTs())) { - Change.Id id = rsrc.getChange().getId(); - bu.addOp(id, addition.op); - bu.execute(); - addition.gatherResults(); - } - return addition.result; - } - - public Addition prepareApplication( - ChangeResource rsrc, AddReviewerInput input, boolean allowGroup) - throws OrmException, IOException, PermissionBackendException, ConfigInvalidException { - String reviewer = input.reviewer; - ReviewerState state = input.state(); - NotifyHandling notify = input.notify; - ListMultimap<RecipientType, Account.Id> accountsToNotify = null; - try { - accountsToNotify = notifyUtil.resolveAccounts(input.notifyDetails); - } catch (BadRequestException e) { - return fail(reviewer, e.getMessage()); - } - boolean confirmed = input.confirmed(); - boolean allowByEmail = projectCache.checkedGet(rsrc.getProject()).isEnableReviewerByEmail(); - - Addition byAccountId = - addByAccountId(reviewer, rsrc, state, notify, accountsToNotify, allowGroup, allowByEmail); - - Addition wholeGroup = null; - if (byAccountId == null || !byAccountId.exactMatchFound) { - wholeGroup = - addWholeGroup( - reviewer, rsrc, state, notify, accountsToNotify, confirmed, allowGroup, allowByEmail); - if (wholeGroup != null && wholeGroup.exactMatchFound) { - return wholeGroup; - } - } - - if (byAccountId != null) { - return byAccountId; - } - if (wholeGroup != null) { - return wholeGroup; - } - - return addByEmail(reviewer, rsrc, state, notify, accountsToNotify); - } - - Addition ccCurrentUser(CurrentUser user, RevisionResource revision) { - return new Addition( - user.getUserName(), - revision.getChangeResource(), - ImmutableSet.of(user.getAccountId()), - null, - CC, - NotifyHandling.NONE, - ImmutableListMultimap.of(), - true); - } - - @Nullable - private Addition addByAccountId( - String reviewer, - ChangeResource rsrc, - ReviewerState state, - NotifyHandling notify, - ListMultimap<RecipientType, Account.Id> accountsToNotify, - boolean allowGroup, - boolean allowByEmail) - throws OrmException, PermissionBackendException, IOException, ConfigInvalidException { - IdentifiedUser user = null; - boolean exactMatchFound = false; - try { - user = accounts.parse(reviewer); - if (reviewer.equalsIgnoreCase(user.getName()) - || reviewer.equals(String.valueOf(user.getAccountId()))) { - exactMatchFound = true; - } - } catch (UnprocessableEntityException | AuthException e) { - // AuthException won't occur since the user is authenticated at this point. - if (!allowGroup && !allowByEmail) { - // Only return failure if we aren't going to try other interpretations. - return fail( - reviewer, MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, reviewer)); - } - return null; - } - - ReviewerResource rrsrc = reviewerFactory.create(rsrc, user.getAccountId()); - Account member = rrsrc.getReviewerUser().getAccount(); - PermissionBackend.ForRef perm = - permissionBackend.user(rrsrc.getReviewerUser()).ref(rrsrc.getChange().getDest()); - if (isValidReviewer(member, perm)) { - return new Addition( - reviewer, - rsrc, - ImmutableSet.of(member.getId()), - null, - state, - notify, - accountsToNotify, - exactMatchFound); - } - if (!member.isActive()) { - if (allowByEmail && state == CC) { - return null; - } - return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInactive, reviewer)); - } - return fail( - reviewer, MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, reviewer)); - } - - @Nullable - private Addition addWholeGroup( - String reviewer, - ChangeResource rsrc, - ReviewerState state, - NotifyHandling notify, - ListMultimap<RecipientType, Account.Id> accountsToNotify, - boolean confirmed, - boolean allowGroup, - boolean allowByEmail) - throws OrmException, IOException, PermissionBackendException { - if (!allowGroup) { - return null; - } - - GroupDescription.Basic group = null; - try { - group = groupsCollection.parseInternal(reviewer); - } catch (UnprocessableEntityException e) { - if (!allowByEmail) { - return fail( - reviewer, - MessageFormat.format(ChangeMessages.get().reviewerNotFoundUserOrGroup, reviewer)); - } - return null; - } - - if (!isLegalReviewerGroup(group.getGroupUUID())) { - return fail( - reviewer, MessageFormat.format(ChangeMessages.get().groupIsNotAllowed, group.getName())); - } - - Set<Account.Id> reviewers = new HashSet<>(); - Set<Account> members; - try { - members = - groupMembersFactory - .create(rsrc.getUser()) - .listAccounts(group.getGroupUUID(), rsrc.getProject()); - } catch (NoSuchGroupException e) { - return fail( - reviewer, - MessageFormat.format(ChangeMessages.get().reviewerNotFoundUserOrGroup, group.getName())); - } catch (NoSuchProjectException e) { - return fail(reviewer, e.getMessage()); - } - - // if maxAllowed is set to 0, it is allowed to add any number of - // reviewers - int maxAllowed = cfg.getInt("addreviewer", "maxAllowed", DEFAULT_MAX_REVIEWERS); - if (maxAllowed > 0 && members.size() > maxAllowed) { - return fail( - reviewer, - MessageFormat.format(ChangeMessages.get().groupHasTooManyMembers, group.getName())); - } - - // if maxWithoutCheck is set to 0, we never ask for confirmation - int maxWithoutConfirmation = - cfg.getInt("addreviewer", "maxWithoutConfirmation", DEFAULT_MAX_REVIEWERS_WITHOUT_CHECK); - if (!confirmed && maxWithoutConfirmation > 0 && members.size() > maxWithoutConfirmation) { - return fail( - reviewer, - true, - MessageFormat.format( - ChangeMessages.get().groupManyMembersConfirmation, group.getName(), members.size())); - } - - PermissionBackend.ForRef perm = - permissionBackend.user(rsrc.getUser()).ref(rsrc.getChange().getDest()); - for (Account member : members) { - if (isValidReviewer(member, perm)) { - reviewers.add(member.getId()); - } - } - - return new Addition(reviewer, rsrc, reviewers, null, state, notify, accountsToNotify, true); - } - - @Nullable - private Addition addByEmail( - String reviewer, - ChangeResource rsrc, - ReviewerState state, - NotifyHandling notify, - ListMultimap<RecipientType, Account.Id> accountsToNotify) - throws PermissionBackendException { - if (!permissionBackend - .user(anonymousProvider) - .change(rsrc.getNotes()) - .database(dbProvider) - .test(ChangePermission.READ)) { - return fail( - reviewer, MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, reviewer)); - } - if (!migration.readChanges()) { - // addByEmail depends on NoteDb. - return fail( - reviewer, MessageFormat.format(ChangeMessages.get().reviewerNotFoundUser, reviewer)); - } - Address adr = Address.tryParse(reviewer); - if (adr == null || !validator.isValid(adr.getEmail())) { - return fail(reviewer, MessageFormat.format(ChangeMessages.get().reviewerInvalid, reviewer)); - } - return new Addition( - reviewer, rsrc, null, ImmutableList.of(adr), state, notify, accountsToNotify, true); - } - - private boolean isValidReviewer(Account member, PermissionBackend.ForRef perm) - throws PermissionBackendException { - if (member.isActive()) { - IdentifiedUser user = identifiedUserFactory.create(member.getId()); - // Does not account for draft status as a user might want to let a - // reviewer see a draft. - try { - perm.user(user).check(RefPermission.READ); - return true; - } catch (AuthException e) { - return false; - } - } - return false; - } - - private Addition fail(String reviewer, String error) { - return fail(reviewer, false, error); - } - - private Addition fail(String reviewer, boolean confirm, String error) { - Addition addition = new Addition(reviewer); - addition.result.confirm = confirm ? true : null; - addition.result.error = error; - return addition; - } - - public class Addition { - final AddReviewerResult result; - final PostReviewersOp op; - final Set<Account.Id> reviewers; - final Collection<Address> reviewersByEmail; - final ReviewerState state; - final ChangeNotes notes; - final IdentifiedUser caller; - final boolean exactMatchFound; - - Addition(String reviewer) { - result = new AddReviewerResult(reviewer); - op = null; - reviewers = ImmutableSet.of(); - reviewersByEmail = ImmutableSet.of(); - state = REVIEWER; - notes = null; - caller = null; - exactMatchFound = false; - } - - protected Addition( - String reviewer, - ChangeResource rsrc, - @Nullable Set<Account.Id> reviewers, - @Nullable Collection<Address> reviewersByEmail, - ReviewerState state, - @Nullable NotifyHandling notify, - ListMultimap<RecipientType, Account.Id> accountsToNotify, - boolean exactMatchFound) { - checkArgument( - reviewers != null || reviewersByEmail != null, - "must have either reviewers or reviewersByEmail"); - - result = new AddReviewerResult(reviewer); - this.reviewers = reviewers == null ? ImmutableSet.of() : reviewers; - this.reviewersByEmail = reviewersByEmail == null ? ImmutableList.of() : reviewersByEmail; - this.state = state; - notes = rsrc.getNotes(); - caller = rsrc.getUser().asIdentifiedUser(); - op = - postReviewersOpFactory.create( - this.reviewers, this.reviewersByEmail, state, notify, accountsToNotify); - this.exactMatchFound = exactMatchFound; - } - - void gatherResults() throws OrmException, PermissionBackendException { - if (notes == null || caller == null) { - // When notes or caller is missing this is likely just carrying an error message - // in the contained AddReviewerResult. - return; - } - - ChangeData cd = changeDataFactory.create(dbProvider.get(), notes); - PermissionBackend.ForChange perm = - permissionBackend.user(caller).database(dbProvider).change(cd); - - // Generate result details and fill AccountLoader. This occurs outside - // the Op because the accounts are in a different table. - PostReviewersOp.Result opResult = op.getResult(); - if (migration.readChanges() && state == CC) { - result.ccs = Lists.newArrayListWithCapacity(opResult.addedCCs().size()); - for (Account.Id accountId : opResult.addedCCs()) { - IdentifiedUser u = identifiedUserFactory.create(accountId); - result.ccs.add(json.format(caller, new ReviewerInfo(accountId.get()), perm.user(u), cd)); - } - accountLoaderFactory.create(true).fill(result.ccs); - for (Address a : reviewersByEmail) { - result.ccs.add(new AccountInfo(a.getName(), a.getEmail())); - } - } else { - result.reviewers = Lists.newArrayListWithCapacity(opResult.addedReviewers().size()); - for (PatchSetApproval psa : opResult.addedReviewers()) { - // New reviewers have value 0, don't bother normalizing. - IdentifiedUser u = identifiedUserFactory.create(psa.getAccountId()); - result.reviewers.add( - json.format( - caller, - new ReviewerInfo(psa.getAccountId().get()), - perm.user(u), - cd, - ImmutableList.of(psa))); - } - accountLoaderFactory.create(true).fill(result.reviewers); - for (Address a : reviewersByEmail) { - result.reviewers.add(ReviewerInfo.byEmail(a.getName(), a.getEmail())); - } - } - } - } - - public static boolean isLegalReviewerGroup(AccountGroup.UUID groupUUID) { - return !SystemGroupBackend.isSystemGroup(groupUUID); - } -} |