// 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.group; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gerrit.common.data.GroupDescription; 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.RestModifyView; 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.AccountGroupById; import com.google.gerrit.reviewdb.client.AccountGroupByIdAud; import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.account.GroupIncludeCache; import com.google.gerrit.server.group.AddIncludedGroups.Input; import com.google.gerrit.server.util.TimeUtil; import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import java.util.List; import java.util.Map; @Singleton public class DeleteIncludedGroups implements RestModifyView<GroupResource, Input> { private final GroupsCollection groupsCollection; private final GroupIncludeCache groupIncludeCache; private final Provider<ReviewDb> db; private final Provider<CurrentUser> self; @Inject DeleteIncludedGroups(GroupsCollection groupsCollection, GroupIncludeCache groupIncludeCache, Provider<ReviewDb> db, Provider<CurrentUser> self) { this.groupsCollection = groupsCollection; this.groupIncludeCache = groupIncludeCache; this.db = db; this.self = self; } @Override public Response<?> apply(GroupResource resource, Input input) throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException { AccountGroup internalGroup = resource.toAccountGroup(); if (internalGroup == null) { throw new MethodNotAllowedException(); } input = Input.init(input); final GroupControl control = resource.getControl(); final Map<AccountGroup.UUID, AccountGroupById> includedGroups = getIncludedGroups(internalGroup.getId()); final List<AccountGroupById> toRemove = Lists.newLinkedList(); for (final String includedGroup : input.groups) { GroupDescription.Basic d = groupsCollection.parse(includedGroup); if (!control.canRemoveGroup(d.getGroupUUID())) { throw new AuthException(String.format("Cannot delete group: %s", d.getName())); } AccountGroupById g = includedGroups.remove(d.getGroupUUID()); if (g != null) { toRemove.add(g); } } if (!toRemove.isEmpty()) { writeAudits(toRemove); db.get().accountGroupById().delete(toRemove); for (final AccountGroupById g : toRemove) { groupIncludeCache.evictMemberIn(g.getIncludeUUID()); } groupIncludeCache.evictMembersOf(internalGroup.getGroupUUID()); } return Response.none(); } private Map<AccountGroup.UUID, AccountGroupById> getIncludedGroups( final AccountGroup.Id groupId) throws OrmException { final Map<AccountGroup.UUID, AccountGroupById> groups = Maps.newHashMap(); for (AccountGroupById g : db.get().accountGroupById().byGroup(groupId)) { groups.put(g.getIncludeUUID(), g); } return groups; } private void writeAudits(final List<AccountGroupById> toBeRemoved) throws OrmException { final Account.Id me = ((IdentifiedUser) self.get()).getAccountId(); final List<AccountGroupByIdAud> auditUpdates = Lists.newLinkedList(); for (final AccountGroupById g : toBeRemoved) { AccountGroupByIdAud audit = null; for (AccountGroupByIdAud a : db.get() .accountGroupByIdAud().byGroupInclude(g.getGroupId(), g.getIncludeUUID())) { if (a.isActive()) { audit = a; break; } } if (audit != null) { audit.removed(me, TimeUtil.nowTs()); auditUpdates.add(audit); } } db.get().accountGroupByIdAud().update(auditUpdates); } @Singleton static class DeleteIncludedGroup implements RestModifyView<IncludedGroupResource, DeleteIncludedGroup.Input> { static class Input { } private final Provider<DeleteIncludedGroups> delete; @Inject DeleteIncludedGroup(final Provider<DeleteIncludedGroups> delete) { this.delete = delete; } @Override public Response<?> apply(IncludedGroupResource resource, Input input) throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException { AddIncludedGroups.Input in = new AddIncludedGroups.Input(); in.groups = ImmutableList.of(resource.getMember().get()); return delete.get().apply(resource, in); } } }