diff options
Diffstat (limited to 'java/com/google/gerrit/server/restapi/group/CreateGroup.java')
-rw-r--r-- | java/com/google/gerrit/server/restapi/group/CreateGroup.java | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java new file mode 100644 index 0000000000..05721146b2 --- /dev/null +++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java @@ -0,0 +1,226 @@ +// 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.restapi.group; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.common.data.GroupDescription; +import com.google.gerrit.extensions.annotations.RequiresCapability; +import com.google.gerrit.extensions.api.groups.GroupInput; +import com.google.gerrit.extensions.client.ListGroupsOption; +import com.google.gerrit.extensions.common.GroupInfo; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.IdString; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestCollectionCreateView; +import com.google.gerrit.extensions.restapi.TopLevelResource; +import com.google.gerrit.extensions.restapi.UnprocessableEntityException; +import com.google.gerrit.extensions.restapi.Url; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AccountGroup; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.Sequences; +import com.google.gerrit.server.UserInitiated; +import com.google.gerrit.server.account.CreateGroupArgs; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.account.GroupUUID; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.group.GroupResolver; +import com.google.gerrit.server.group.GroupResource; +import com.google.gerrit.server.group.InternalGroup; +import com.google.gerrit.server.group.InternalGroupDescription; +import com.google.gerrit.server.group.SystemGroupBackend; +import com.google.gerrit.server.group.db.GroupsUpdate; +import com.google.gerrit.server.group.db.InternalGroupCreation; +import com.google.gerrit.server.group.db.InternalGroupUpdate; +import com.google.gerrit.server.permissions.PermissionBackendException; +import com.google.gerrit.server.plugincontext.PluginSetContext; +import com.google.gerrit.server.validators.GroupCreationValidationListener; +import com.google.gerrit.server.validators.ValidationException; +import com.google.gwtorm.server.OrmDuplicateKeyException; +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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; + +@RequiresCapability(GlobalCapability.CREATE_GROUP) +@Singleton +public class CreateGroup + implements RestCollectionCreateView<TopLevelResource, GroupResource, GroupInput> { + private final Provider<IdentifiedUser> self; + private final PersonIdent serverIdent; + private final Provider<GroupsUpdate> groupsUpdateProvider; + private final GroupCache groupCache; + private final GroupResolver groups; + private final GroupJson json; + private final PluginSetContext<GroupCreationValidationListener> groupCreationValidationListeners; + private final AddMembers addMembers; + private final SystemGroupBackend systemGroupBackend; + private final boolean defaultVisibleToAll; + private final Sequences sequences; + + @Inject + CreateGroup( + Provider<IdentifiedUser> self, + @GerritPersonIdent PersonIdent serverIdent, + @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider, + GroupCache groupCache, + GroupResolver groups, + GroupJson json, + PluginSetContext<GroupCreationValidationListener> groupCreationValidationListeners, + AddMembers addMembers, + SystemGroupBackend systemGroupBackend, + @GerritServerConfig Config cfg, + Sequences sequences) { + this.self = self; + this.serverIdent = serverIdent; + this.groupsUpdateProvider = groupsUpdateProvider; + this.groupCache = groupCache; + this.groups = groups; + this.json = json; + this.groupCreationValidationListeners = groupCreationValidationListeners; + this.addMembers = addMembers; + this.systemGroupBackend = systemGroupBackend; + this.defaultVisibleToAll = cfg.getBoolean("groups", "newGroupsVisibleToAll", false); + this.sequences = sequences; + } + + public CreateGroup addOption(ListGroupsOption o) { + json.addOption(o); + return this; + } + + public CreateGroup addOptions(Collection<ListGroupsOption> o) { + json.addOptions(o); + return this; + } + + @Override + public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input) + throws AuthException, BadRequestException, UnprocessableEntityException, + ResourceConflictException, OrmException, IOException, ConfigInvalidException, + ResourceNotFoundException, PermissionBackendException { + String name = id.get(); + if (input == null) { + input = new GroupInput(); + } + if (input.name != null && !name.equals(input.name)) { + throw new BadRequestException("name must match URL"); + } + + AccountGroup.UUID ownerUuid = owner(input); + CreateGroupArgs args = new CreateGroupArgs(); + args.setGroupName(name); + args.groupDescription = Strings.emptyToNull(input.description); + args.visibleToAll = MoreObjects.firstNonNull(input.visibleToAll, defaultVisibleToAll); + args.ownerGroupUuid = ownerUuid; + if (input.members != null && !input.members.isEmpty()) { + List<Account.Id> members = new ArrayList<>(); + for (String nameOrEmailOrId : input.members) { + Account a = addMembers.findAccount(nameOrEmailOrId); + if (!a.isActive()) { + throw new UnprocessableEntityException( + String.format("Account Inactive: %s", nameOrEmailOrId)); + } + members.add(a.getId()); + } + args.initialMembers = members; + } else { + args.initialMembers = + ownerUuid == null + ? Collections.singleton(self.get().getAccountId()) + : Collections.<Account.Id>emptySet(); + } + + try { + groupCreationValidationListeners.runEach( + l -> l.validateNewGroup(args), ValidationException.class); + } catch (ValidationException e) { + throw new ResourceConflictException(e.getMessage(), e); + } + + return json.format(new InternalGroupDescription(createGroup(args))); + } + + private AccountGroup.UUID owner(GroupInput input) throws UnprocessableEntityException { + if (input.ownerId != null) { + GroupDescription.Internal d = groups.parseInternal(Url.decode(input.ownerId)); + return d.getGroupUUID(); + } + return null; + } + + private InternalGroup createGroup(CreateGroupArgs createGroupArgs) + throws OrmException, ResourceConflictException, IOException, ConfigInvalidException { + + String nameLower = createGroupArgs.getGroupName().toLowerCase(Locale.US); + + for (String name : systemGroupBackend.getNames()) { + if (name.toLowerCase(Locale.US).equals(nameLower)) { + throw new ResourceConflictException("group '" + name + "' already exists"); + } + } + + for (String name : systemGroupBackend.getReservedNames()) { + if (name.toLowerCase(Locale.US).equals(nameLower)) { + throw new ResourceConflictException("group name '" + name + "' is reserved"); + } + } + + AccountGroup.Id groupId = new AccountGroup.Id(sequences.nextGroupId()); + AccountGroup.UUID uuid = + GroupUUID.make( + createGroupArgs.getGroupName(), + self.get().newCommitterIdent(serverIdent.getWhen(), serverIdent.getTimeZone())); + InternalGroupCreation groupCreation = + InternalGroupCreation.builder() + .setGroupUUID(uuid) + .setNameKey(createGroupArgs.getGroup()) + .setId(groupId) + .build(); + InternalGroupUpdate.Builder groupUpdateBuilder = + InternalGroupUpdate.builder().setVisibleToAll(createGroupArgs.visibleToAll); + if (createGroupArgs.ownerGroupUuid != null) { + Optional<InternalGroup> ownerGroup = groupCache.get(createGroupArgs.ownerGroupUuid); + ownerGroup.map(InternalGroup::getGroupUUID).ifPresent(groupUpdateBuilder::setOwnerGroupUUID); + } + if (createGroupArgs.groupDescription != null) { + groupUpdateBuilder.setDescription(createGroupArgs.groupDescription); + } + groupUpdateBuilder.setMemberModification( + members -> ImmutableSet.copyOf(createGroupArgs.initialMembers)); + try { + return groupsUpdateProvider.get().createGroup(groupCreation, groupUpdateBuilder.build()); + } catch (OrmDuplicateKeyException e) { + throw new ResourceConflictException( + "group '" + createGroupArgs.getGroupName() + "' already exists"); + } + } +} |