LCOV - code coverage report
Current view: top level - server/restapi/project - CreateAccessChange.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 66 71 93.0 %
Date: 2022-11-19 15:00:39 Functions: 4 4 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 static com.google.gerrit.server.project.ProjectCache.illegalState;
      18             : 
      19             : import com.google.common.collect.ImmutableList;
      20             : import com.google.common.collect.ImmutableMap;
      21             : import com.google.gerrit.entities.AccessSection;
      22             : import com.google.gerrit.entities.Change;
      23             : import com.google.gerrit.entities.PatchSet;
      24             : import com.google.gerrit.entities.Project;
      25             : import com.google.gerrit.entities.RefNames;
      26             : import com.google.gerrit.exceptions.InvalidNameException;
      27             : import com.google.gerrit.extensions.api.access.ProjectAccessInput;
      28             : import com.google.gerrit.extensions.common.ChangeInfo;
      29             : import com.google.gerrit.extensions.restapi.AuthException;
      30             : import com.google.gerrit.extensions.restapi.BadRequestException;
      31             : import com.google.gerrit.extensions.restapi.Response;
      32             : import com.google.gerrit.extensions.restapi.RestApiException;
      33             : import com.google.gerrit.extensions.restapi.RestModifyView;
      34             : import com.google.gerrit.server.approval.ApprovalsUtil;
      35             : import com.google.gerrit.server.change.ChangeInserter;
      36             : import com.google.gerrit.server.change.ChangeJson;
      37             : import com.google.gerrit.server.git.meta.MetaDataUpdate;
      38             : import com.google.gerrit.server.notedb.Sequences;
      39             : import com.google.gerrit.server.permissions.PermissionBackend;
      40             : import com.google.gerrit.server.permissions.PermissionBackendException;
      41             : import com.google.gerrit.server.permissions.ProjectPermission;
      42             : import com.google.gerrit.server.permissions.RefPermission;
      43             : import com.google.gerrit.server.project.ProjectCache;
      44             : import com.google.gerrit.server.project.ProjectConfig;
      45             : import com.google.gerrit.server.project.ProjectResource;
      46             : import com.google.gerrit.server.update.BatchUpdate;
      47             : import com.google.gerrit.server.update.UpdateException;
      48             : import com.google.gerrit.server.util.time.TimeUtil;
      49             : import com.google.inject.Inject;
      50             : import com.google.inject.Provider;
      51             : import com.google.inject.Singleton;
      52             : import java.io.IOException;
      53             : import org.eclipse.jgit.errors.ConfigInvalidException;
      54             : import org.eclipse.jgit.lib.ObjectId;
      55             : import org.eclipse.jgit.lib.ObjectInserter;
      56             : import org.eclipse.jgit.lib.ObjectReader;
      57             : import org.eclipse.jgit.revwalk.RevCommit;
      58             : import org.eclipse.jgit.revwalk.RevWalk;
      59             : 
      60             : @Singleton
      61             : public class CreateAccessChange implements RestModifyView<ProjectResource, ProjectAccessInput> {
      62             :   private final PermissionBackend permissionBackend;
      63             :   private final Sequences seq;
      64             :   private final ChangeInserter.Factory changeInserterFactory;
      65             :   private final BatchUpdate.Factory updateFactory;
      66             :   private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
      67             :   private final SetAccessUtil setAccess;
      68             :   private final ChangeJson.Factory jsonFactory;
      69             :   private final ProjectCache projectCache;
      70             :   private final ProjectConfig.Factory projectConfigFactory;
      71             : 
      72             :   @Inject
      73             :   CreateAccessChange(
      74             :       PermissionBackend permissionBackend,
      75             :       ChangeInserter.Factory changeInserterFactory,
      76             :       BatchUpdate.Factory updateFactory,
      77             :       Sequences seq,
      78             :       Provider<MetaDataUpdate.User> metaDataUpdateFactory,
      79             :       SetAccessUtil accessUtil,
      80             :       ChangeJson.Factory jsonFactory,
      81             :       ProjectCache projectCache,
      82         146 :       ProjectConfig.Factory projectConfigFactory) {
      83         146 :     this.permissionBackend = permissionBackend;
      84         146 :     this.seq = seq;
      85         146 :     this.changeInserterFactory = changeInserterFactory;
      86         146 :     this.updateFactory = updateFactory;
      87         146 :     this.metaDataUpdateFactory = metaDataUpdateFactory;
      88         146 :     this.setAccess = accessUtil;
      89         146 :     this.jsonFactory = jsonFactory;
      90         146 :     this.projectCache = projectCache;
      91         146 :     this.projectConfigFactory = projectConfigFactory;
      92         146 :   }
      93             : 
      94             :   @Override
      95             :   public Response<ChangeInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
      96             :       throws PermissionBackendException, AuthException, IOException, ConfigInvalidException,
      97             :           InvalidNameException, UpdateException, RestApiException {
      98           2 :     PermissionBackend.ForProject forProject =
      99           2 :         permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey());
     100           2 :     if (!check(forProject, ProjectPermission.READ_CONFIG)) {
     101           0 :       throw new AuthException(RefNames.REFS_CONFIG + " not visible");
     102             :     }
     103           2 :     if (!check(forProject, ProjectPermission.WRITE_CONFIG)) {
     104             :       try {
     105           1 :         forProject.ref(RefNames.REFS_CONFIG).check(RefPermission.CREATE_CHANGE);
     106           0 :       } catch (AuthException denied) {
     107           0 :         throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG, denied);
     108           1 :       }
     109             :     }
     110           2 :     projectCache
     111           2 :         .get(rsrc.getNameKey())
     112           2 :         .orElseThrow(illegalState(rsrc.getNameKey()))
     113           2 :         .checkStatePermitsWrite();
     114             : 
     115           2 :     MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
     116           2 :     ImmutableList<AccessSection> removals =
     117           2 :         setAccess.getAccessSections(input.remove, /* rejectNonResolvableGroups= */ false);
     118           2 :     ImmutableList<AccessSection> additions =
     119           2 :         setAccess.getAccessSections(input.add, /* rejectNonResolvableGroups= */ true);
     120             : 
     121             :     Project.NameKey newParentProjectName =
     122           2 :         input.parent == null ? null : Project.nameKey(input.parent);
     123             : 
     124           2 :     try (MetaDataUpdate md = metaDataUpdateUser.create(rsrc.getNameKey())) {
     125           2 :       ProjectConfig config = projectConfigFactory.read(md);
     126           2 :       ObjectId oldCommit = config.getRevision();
     127           2 :       String oldCommitSha1 = oldCommit == null ? null : oldCommit.getName();
     128             : 
     129           2 :       setAccess.validateChanges(config, removals, additions);
     130           2 :       setAccess.applyChanges(config, removals, additions);
     131             :       try {
     132           2 :         setAccess.setParentName(
     133           2 :             rsrc.getUser().asIdentifiedUser(),
     134             :             config,
     135           2 :             rsrc.getNameKey(),
     136             :             newParentProjectName,
     137             :             false);
     138           0 :       } catch (AuthException e) {
     139           0 :         throw new IllegalStateException(e);
     140           2 :       }
     141             : 
     142           2 :       md.setMessage("Review access change");
     143           2 :       md.setInsertChangeId(true);
     144           2 :       Change.Id changeId = Change.id(seq.nextChangeId());
     145             : 
     146           2 :       RevCommit commit =
     147           2 :           config.commitToNewRef(md, PatchSet.id(changeId, Change.INITIAL_PATCH_SET_ID).toRefName());
     148             : 
     149           2 :       if (commit.name().equals(oldCommitSha1)) {
     150           2 :         throw new BadRequestException("no change");
     151             :       }
     152             : 
     153           1 :       try (ObjectInserter objInserter = md.getRepository().newObjectInserter();
     154           1 :           ObjectReader objReader = objInserter.newReader();
     155           1 :           RevWalk rw = new RevWalk(objReader);
     156           1 :           BatchUpdate bu =
     157           1 :               updateFactory.create(rsrc.getNameKey(), rsrc.getUser(), TimeUtil.now())) {
     158           1 :         bu.setRepository(md.getRepository(), rw, objInserter);
     159           1 :         ChangeInserter ins = newInserter(changeId, commit);
     160           1 :         bu.insertChange(ins);
     161           1 :         bu.execute();
     162           1 :         return Response.created(jsonFactory.noOptions().format(ins.getChange()));
     163             :       }
     164           1 :     } catch (InvalidNameException e) {
     165           1 :       throw new BadRequestException(e.toString());
     166             :     }
     167             :   }
     168             : 
     169             :   // ProjectConfig doesn't currently support fusing into a BatchUpdate.
     170             :   @SuppressWarnings("deprecation")
     171             :   private ChangeInserter newInserter(Change.Id changeId, RevCommit commit) {
     172           1 :     return changeInserterFactory
     173           1 :         .create(changeId, commit, RefNames.REFS_CONFIG)
     174           1 :         .setMessage(
     175             :             // Same message as in ReceiveCommits.CreateRequest.
     176           1 :             ApprovalsUtil.renderMessageWithApprovals(1, ImmutableMap.of(), ImmutableMap.of()))
     177           1 :         .setValidate(false)
     178           1 :         .setUpdateRef(false);
     179             :   }
     180             : 
     181             :   private boolean check(PermissionBackend.ForProject perm, ProjectPermission p)
     182             :       throws PermissionBackendException {
     183             :     try {
     184           2 :       perm.check(p);
     185           2 :       return true;
     186           1 :     } catch (AuthException denied) {
     187           1 :       return false;
     188             :     }
     189             :   }
     190             : }

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