All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.restapi.group.DeleteGroup Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2024 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.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.CachedProjectConfig;
import com.google.gerrit.entities.GroupDescription;
import com.google.gerrit.entities.InternalGroup;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.common.DeleteGroupInput;
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.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
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.server.UserInitiated;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsUpdate;
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;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;

@Singleton
public class DeleteGroup implements RestModifyView {
  private final Provider listGroupProvider;
  private final GroupCache groupCache;
  private final ProjectCache projectCache;
  private final Provider groupsUpdateProvider;
  private final GroupJson json;
  private final Groups groups;
  private final boolean deleteGroupEnabled;

  @Inject
  DeleteGroup(
      Provider listGroupProvider,
      GroupCache groupCache,
      ProjectCache projectCache,
      @UserInitiated Provider groupsUpdateProvider,
      @GerritServerConfig Config cfg,
      GroupJson json,
      Groups groups) {
    this.listGroupProvider = listGroupProvider;
    this.groupCache = groupCache;
    this.projectCache = projectCache;
    this.groupsUpdateProvider = groupsUpdateProvider;
    this.json = json;
    this.groups = groups;
    this.deleteGroupEnabled = cfg.getBoolean("groups", "enableDeleteGroup", false);
  }

  public DeleteGroup addOption(ListGroupsOption o) {
    json.addOption(o);
    return this;
  }

  @Override
  public Response apply(GroupResource resource, DeleteGroupInput input)
      throws AuthException,
          BadRequestException,
          UnprocessableEntityException,
          ResourceConflictException,
          IOException,
          ConfigInvalidException,
          ResourceNotFoundException,
          PermissionBackendException,
          NotInternalGroupException {
    if (deleteGroupEnabled) {
      GroupDescription.Internal internalGroup =
          resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
      final GroupControl control = resource.getControl();
      if (!control.canDeleteGroup()) {
        throw new AuthException("Cannot delete group " + internalGroup.getName());
      }
      groupDeletionPrecondition(internalGroup);
      deleteGroup(internalGroup);
      return Response.ok();
    }
    throw new ResourceNotFoundException("Deletion of Group is not enabled");
  }

  private void groupDeletionPrecondition(GroupDescription.Internal internalGroup)
      throws ResourceConflictException, ConfigInvalidException, IOException {
    AccountGroup.UUID uuid = internalGroup.getGroupUUID();
    if (groupCache.get(internalGroup.getGroupUUID()).isEmpty()) {
      throw new ResourceConflictException(
          String.format("group %s does not exist", internalGroup.getGroupUUID()));
    }
    List ownedGroup = getOwnedGroup(uuid, internalGroup.getName());
    if (!ownedGroup.isEmpty()) {
      String msg =
          "Cannot delete group that is owner of other groups: \n"
              + ownedGroup.stream()
                  .map(InternalGroup::getName)
                  .collect(Collectors.joining(", ", "[", "]"));
      throw new ResourceConflictException(msg);
    }
    List inProjects = getProjectsWithGroupRefs(uuid);
    if (!inProjects.isEmpty()) {
      String msg =
          "Cannot delete group that is referenced in access permissions for project: \n"
              + inProjects;
      throw new ResourceConflictException(msg);
    }
    List subgroupsInGroups = getSubgroupsInGroups(uuid, internalGroup.getName());
    if (!subgroupsInGroups.isEmpty()) {
      String msg = "Cannot delete group that is subgroup of another group: \n" + subgroupsInGroups;
      throw new ResourceConflictException(msg);
    }
  }

  private List getOwnedGroup(AccountGroup.UUID uuid, String groupOwnerName)
      throws IOException {
    try {
      ListGroups listOwner = listGroupProvider.get();
      listOwner.setOwnedBy(uuid.get());
      List groups = listOwner.get();
      return groups.stream()
          .filter(group -> !group.name.equals(groupOwnerName))
          .map(group -> groupCache.get(AccountGroup.UUID.parse(group.id)))
          .filter(Optional::isPresent)
          .map(Optional::get)
          .collect(Collectors.toList());
    } catch (Exception e) {
      throw new IOException("Failed to check owned groups for group " + uuid.get(), e);
    }
  }

  private List getProjectsWithGroupRefs(AccountGroup.UUID uuid) {
    List projects = new ArrayList<>();
    for (Project.NameKey projectName : projectCache.all()) {
      Optional projectState = projectCache.get(projectName);
      if (projectState.isPresent()) {
        CachedProjectConfig config = projectState.get().getConfig();
        if (config.getGroup(uuid).isPresent()) {
          projects.add(projectName.toString());
        }
      }
    }
    return projects;
  }

  private List getSubgroupsInGroups(AccountGroup.UUID uuid, String groupName)
      throws ConfigInvalidException, IOException {
    List allGroupsWithSubGroups = new ArrayList<>();
    groups
        .getAllGroupReferences()
        .forEach(
            entry -> {
              try {
                if (groups.getGroup(entry.getUUID()).isEmpty()) {
                  throw new ResourceNotFoundException(
                      String.format(
                          "Could not check if group %s is subgroup of %s",
                          groupName, entry.getName()));
                }
                if (groups.getGroup(entry.getUUID()).get().getSubgroups().contains(uuid)) {
                  allGroupsWithSubGroups.add(entry.getName());
                }
              } catch (IOException | ConfigInvalidException | ResourceNotFoundException e) {
                throw new RuntimeException(e);
              }
            });
    return allGroupsWithSubGroups;
  }

  private void deleteGroup(GroupDescription.Internal internalGroup)
      throws IOException, ConfigInvalidException {
    AccountGroup.UUID uuid = internalGroup.getGroupUUID();
    groupsUpdateProvider.get().deleteGroup(uuid);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy