LCOV - code coverage report
Current view: top level - server/restapi/project - SetAccessUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 116 125 92.8 %
Date: 2022-11-19 15:00:39 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2017 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.restapi.project;
      16             : 
      17             : import com.google.common.collect.ImmutableList;
      18             : import com.google.common.collect.Iterables;
      19             : import com.google.gerrit.common.data.GlobalCapability;
      20             : import com.google.gerrit.entities.AccessSection;
      21             : import com.google.gerrit.entities.AccountGroup;
      22             : import com.google.gerrit.entities.GroupDescription;
      23             : import com.google.gerrit.entities.GroupReference;
      24             : import com.google.gerrit.entities.LabelType;
      25             : import com.google.gerrit.entities.Permission;
      26             : import com.google.gerrit.entities.PermissionRule;
      27             : import com.google.gerrit.entities.Project;
      28             : import com.google.gerrit.exceptions.InvalidNameException;
      29             : import com.google.gerrit.extensions.api.access.AccessSectionInfo;
      30             : import com.google.gerrit.extensions.api.access.PermissionInfo;
      31             : import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
      32             : import com.google.gerrit.extensions.restapi.AuthException;
      33             : import com.google.gerrit.extensions.restapi.BadRequestException;
      34             : import com.google.gerrit.extensions.restapi.ResourceConflictException;
      35             : import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
      36             : import com.google.gerrit.server.IdentifiedUser;
      37             : import com.google.gerrit.server.config.AllProjectsName;
      38             : import com.google.gerrit.server.group.GroupResolver;
      39             : import com.google.gerrit.server.permissions.PermissionBackendException;
      40             : import com.google.gerrit.server.permissions.PluginPermissionsUtil;
      41             : import com.google.gerrit.server.project.ProjectConfig;
      42             : import com.google.gerrit.server.project.RefPattern;
      43             : import com.google.inject.Inject;
      44             : import com.google.inject.Provider;
      45             : import com.google.inject.Singleton;
      46             : import java.util.List;
      47             : import java.util.Map;
      48             : import java.util.Set;
      49             : 
      50             : @Singleton
      51             : public class SetAccessUtil {
      52             :   private final GroupResolver groupResolver;
      53             :   private final AllProjectsName allProjects;
      54             :   private final Provider<SetParent> setParent;
      55             :   private final PluginPermissionsUtil pluginPermissionsUtil;
      56             : 
      57             :   @Inject
      58             :   private SetAccessUtil(
      59             :       GroupResolver groupResolver,
      60             :       AllProjectsName allProjects,
      61             :       Provider<SetParent> setParent,
      62         146 :       PluginPermissionsUtil pluginPermissionsUtil) {
      63         146 :     this.groupResolver = groupResolver;
      64         146 :     this.allProjects = allProjects;
      65         146 :     this.setParent = setParent;
      66         146 :     this.pluginPermissionsUtil = pluginPermissionsUtil;
      67         146 :   }
      68             : 
      69             :   ImmutableList<AccessSection> getAccessSections(
      70             :       Map<String, AccessSectionInfo> sectionInfos, boolean rejectNonResolvableGroups)
      71             :       throws UnprocessableEntityException {
      72           8 :     if (sectionInfos == null) {
      73           7 :       return ImmutableList.of();
      74             :     }
      75             : 
      76           7 :     ImmutableList.Builder<AccessSection> sections =
      77           7 :         ImmutableList.builderWithExpectedSize(sectionInfos.size());
      78           7 :     for (Map.Entry<String, AccessSectionInfo> entry : sectionInfos.entrySet()) {
      79           7 :       if (entry.getValue().permissions == null) {
      80           0 :         continue;
      81             :       }
      82             : 
      83           7 :       AccessSection.Builder accessSection = AccessSection.builder(entry.getKey());
      84             :       for (Map.Entry<String, PermissionInfo> permissionEntry :
      85           7 :           entry.getValue().permissions.entrySet()) {
      86           7 :         if (permissionEntry.getValue().rules == null) {
      87           0 :           continue;
      88             :         }
      89             : 
      90           7 :         Permission.Builder p = Permission.builder(permissionEntry.getKey());
      91           7 :         if (permissionEntry.getValue().exclusive != null) {
      92           1 :           p.setExclusiveGroup(permissionEntry.getValue().exclusive);
      93             :         }
      94             : 
      95             :         for (Map.Entry<String, PermissionRuleInfo> permissionRuleInfoEntry :
      96           7 :             permissionEntry.getValue().rules.entrySet()) {
      97           7 :           GroupDescription.Basic group = groupResolver.parseId(permissionRuleInfoEntry.getKey());
      98             :           GroupReference groupReference;
      99           7 :           if (group != null) {
     100           7 :             groupReference = GroupReference.forGroup(group);
     101             :           } else {
     102           1 :             if (rejectNonResolvableGroups) {
     103           0 :               throw new UnprocessableEntityException(
     104           0 :                   permissionRuleInfoEntry.getKey() + " is not a valid group ID");
     105             :             }
     106           1 :             AccountGroup.UUID uuid = AccountGroup.UUID.parse(permissionRuleInfoEntry.getKey());
     107           1 :             groupReference = GroupReference.create(uuid, uuid.get());
     108             :           }
     109             : 
     110           7 :           PermissionRuleInfo pri = permissionRuleInfoEntry.getValue();
     111           7 :           PermissionRule.Builder r = PermissionRule.builder(groupReference);
     112           7 :           if (pri != null) {
     113           7 :             if (pri.max != null) {
     114           1 :               r.setMax(pri.max);
     115             :             }
     116           7 :             if (pri.min != null) {
     117           1 :               r.setMin(pri.min);
     118             :             }
     119           7 :             if (pri.action != null) {
     120           7 :               r.setAction(GetAccess.ACTION_TYPE.inverse().get(pri.action));
     121             :             }
     122           7 :             if (pri.force != null) {
     123           7 :               r.setForce(pri.force);
     124             :             }
     125             :           }
     126           7 :           p.add(r);
     127           7 :         }
     128           7 :         accessSection.addPermission(p);
     129           7 :       }
     130           7 :       sections.add(accessSection.build());
     131           7 :     }
     132           7 :     return sections.build();
     133             :   }
     134             : 
     135             :   /**
     136             :    * Checks that the removals and additions are logically valid, but doesn't check current user's
     137             :    * permission.
     138             :    */
     139             :   void validateChanges(
     140             :       ProjectConfig config, List<AccessSection> removals, List<AccessSection> additions)
     141             :       throws BadRequestException, InvalidNameException {
     142             :     // Perform permission checks
     143           8 :     for (AccessSection section : Iterables.concat(additions, removals)) {
     144           7 :       boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(section.getName());
     145           7 :       if (isGlobalCapabilities) {
     146           3 :         if (!allProjects.equals(config.getName())) {
     147           1 :           throw new BadRequestException(
     148           1 :               "Cannot edit global capabilities for projects other than " + allProjects.get());
     149             :         }
     150             :       }
     151           7 :     }
     152             : 
     153             :     // Perform addition checks
     154           8 :     for (AccessSection section : additions) {
     155           7 :       String name = section.getName();
     156           7 :       boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(name);
     157             : 
     158           7 :       if (!isGlobalCapabilities) {
     159           6 :         if (!AccessSection.isValidRefSectionName(name)) {
     160           0 :           throw new BadRequestException("invalid section name");
     161             :         }
     162           6 :         RefPattern.validate(name);
     163             : 
     164             :         // Check all permissions for soundness
     165           6 :         for (Permission p : section.getPermissions()) {
     166           6 :           if (!isPermission(p.getName())) {
     167           1 :             throw new BadRequestException("Unknown permission: " + p.getName());
     168             :           }
     169           6 :         }
     170             :       } else {
     171             :         // Check all permissions for soundness
     172           3 :         for (Permission p : section.getPermissions()) {
     173           3 :           if (!isCapability(p.getName())) {
     174           1 :             throw new BadRequestException("Unknown global capability: " + p.getName());
     175             :           }
     176           3 :         }
     177             :       }
     178           7 :     }
     179           8 :   }
     180             : 
     181             :   void applyChanges(
     182             :       ProjectConfig config, List<AccessSection> removals, List<AccessSection> additions) {
     183             :     // Apply removals
     184           8 :     for (AccessSection section : removals) {
     185           1 :       if (section.getPermissions().isEmpty()) {
     186             :         // Remove entire section
     187           0 :         config.remove(config.getAccessSection(section.getName()));
     188           0 :         continue;
     189             :       }
     190             : 
     191             :       // Remove specific permissions
     192           1 :       for (Permission p : section.getPermissions()) {
     193           1 :         if (p.getRules().isEmpty()) {
     194           1 :           config.remove(config.getAccessSection(section.getName()), p);
     195             :         } else {
     196           1 :           for (PermissionRule r : p.getRules()) {
     197           1 :             config.remove(config.getAccessSection(section.getName()), p, r);
     198           1 :           }
     199             :         }
     200           1 :       }
     201           1 :     }
     202             : 
     203             :     // Apply additions
     204           8 :     for (AccessSection section : additions) {
     205           7 :       config.upsertAccessSection(
     206           7 :           section.getName(),
     207             :           existingAccessSection -> {
     208           7 :             for (Permission p : section.getPermissions()) {
     209           7 :               Permission currentPermission =
     210           7 :                   existingAccessSection.build().getPermission(p.getName());
     211           7 :               if (currentPermission == null) {
     212             :                 // Add Permission
     213           7 :                 existingAccessSection.addPermission(p.toBuilder());
     214             :               } else {
     215           1 :                 for (PermissionRule r : p.getRules()) {
     216             :                   // AddPermissionRule
     217           1 :                   existingAccessSection.upsertPermission(p.getName()).add(r.toBuilder());
     218           1 :                 }
     219             :               }
     220           7 :             }
     221           7 :           });
     222           7 :     }
     223           8 :   }
     224             : 
     225             :   /**
     226             :    * Updates the parent project in the given config.
     227             :    *
     228             :    * @param identifiedUser the user
     229             :    * @param config the config to modify
     230             :    * @param projectName the project for which to change access.
     231             :    * @param newParentProjectName the new parent to set; passing null will make this a nop
     232             :    * @param checkAdmin if set, verify that user has administrateServer permission
     233             :    */
     234             :   public void setParentName(
     235             :       IdentifiedUser identifiedUser,
     236             :       ProjectConfig config,
     237             :       Project.NameKey projectName,
     238             :       Project.NameKey newParentProjectName,
     239             :       boolean checkAdmin)
     240             :       throws ResourceConflictException, AuthException, PermissionBackendException,
     241             :           BadRequestException {
     242           8 :     if (newParentProjectName != null
     243           1 :         && !config.getProject().getNameKey().equals(allProjects)
     244           1 :         && !config.getProject().getParent(allProjects).equals(newParentProjectName)) {
     245             :       try {
     246           1 :         setParent
     247           1 :             .get()
     248           1 :             .validateParentUpdate(
     249           1 :                 projectName, identifiedUser, newParentProjectName.get(), checkAdmin);
     250           0 :       } catch (UnprocessableEntityException e) {
     251           0 :         throw new ResourceConflictException(e.getMessage(), e);
     252           1 :       }
     253           1 :       config.updateProject(p -> p.setParent(newParentProjectName));
     254             :     }
     255           8 :   }
     256             : 
     257             :   private boolean isPermission(String name) {
     258           6 :     if (Permission.isPermission(name)) {
     259           5 :       if (Permission.isLabel(name) || Permission.isLabelAs(name)) {
     260           1 :         String labelName = Permission.extractLabel(name);
     261             :         try {
     262           1 :           LabelType.checkName(labelName);
     263           1 :         } catch (IllegalArgumentException e) {
     264           1 :           return false;
     265           1 :         }
     266             :       }
     267           5 :       return true;
     268             :     }
     269           2 :     Set<String> pluginPermissions =
     270           2 :         pluginPermissionsUtil.collectPluginProjectPermissions().keySet();
     271           2 :     return pluginPermissions.contains(name);
     272             :   }
     273             : 
     274             :   private boolean isCapability(String name) {
     275           3 :     if (GlobalCapability.isGlobalCapability(name)) {
     276           1 :       return true;
     277             :     }
     278           3 :     Set<String> pluginCapabilities = pluginPermissionsUtil.collectPluginCapabilities().keySet();
     279           3 :     return pluginCapabilities.contains(name);
     280             :   }
     281             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750