LCOV - code coverage report
Current view: top level - server/restapi/project - CreateTag.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 62 66 93.9 %
Date: 2022-11-19 15:00:39 Functions: 4 4 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2016 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 org.eclipse.jgit.lib.Constants.R_TAGS;
      18             : 
      19             : import com.google.common.base.Strings;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.extensions.api.projects.TagInfo;
      22             : import com.google.gerrit.extensions.api.projects.TagInput;
      23             : import com.google.gerrit.extensions.restapi.AuthException;
      24             : import com.google.gerrit.extensions.restapi.BadRequestException;
      25             : import com.google.gerrit.extensions.restapi.IdString;
      26             : import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
      27             : import com.google.gerrit.extensions.restapi.ResourceConflictException;
      28             : import com.google.gerrit.extensions.restapi.Response;
      29             : import com.google.gerrit.extensions.restapi.RestApiException;
      30             : import com.google.gerrit.extensions.restapi.RestCollectionCreateView;
      31             : import com.google.gerrit.server.WebLinks;
      32             : import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
      33             : import com.google.gerrit.server.git.GitRepositoryManager;
      34             : import com.google.gerrit.server.git.TagCache;
      35             : import com.google.gerrit.server.permissions.PermissionBackend;
      36             : import com.google.gerrit.server.permissions.PermissionBackendException;
      37             : import com.google.gerrit.server.permissions.RefPermission;
      38             : import com.google.gerrit.server.project.NoSuchProjectException;
      39             : import com.google.gerrit.server.project.ProjectResource;
      40             : import com.google.gerrit.server.project.RefUtil;
      41             : import com.google.gerrit.server.project.TagResource;
      42             : import com.google.gerrit.server.util.time.TimeUtil;
      43             : import com.google.inject.Inject;
      44             : import com.google.inject.Singleton;
      45             : import java.io.IOException;
      46             : import java.time.ZoneId;
      47             : import org.eclipse.jgit.api.Git;
      48             : import org.eclipse.jgit.api.TagCommand;
      49             : import org.eclipse.jgit.api.errors.GitAPIException;
      50             : import org.eclipse.jgit.lib.Constants;
      51             : import org.eclipse.jgit.lib.ObjectId;
      52             : import org.eclipse.jgit.lib.Ref;
      53             : import org.eclipse.jgit.lib.Repository;
      54             : import org.eclipse.jgit.revwalk.RevObject;
      55             : import org.eclipse.jgit.revwalk.RevWalk;
      56             : 
      57             : @Singleton
      58             : public class CreateTag implements RestCollectionCreateView<ProjectResource, TagResource, TagInput> {
      59         138 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      60             :   private final PermissionBackend permissionBackend;
      61             :   private final GitRepositoryManager repoManager;
      62             :   private final TagCache tagCache;
      63             :   private final GitReferenceUpdated referenceUpdated;
      64             :   private final WebLinks links;
      65             : 
      66             :   @Inject
      67             :   CreateTag(
      68             :       PermissionBackend permissionBackend,
      69             :       GitRepositoryManager repoManager,
      70             :       TagCache tagCache,
      71             :       GitReferenceUpdated referenceUpdated,
      72         138 :       WebLinks webLinks) {
      73         138 :     this.permissionBackend = permissionBackend;
      74         138 :     this.repoManager = repoManager;
      75         138 :     this.tagCache = tagCache;
      76         138 :     this.referenceUpdated = referenceUpdated;
      77         138 :     this.links = webLinks;
      78         138 :   }
      79             : 
      80             :   @Override
      81             :   public Response<TagInfo> apply(ProjectResource resource, IdString id, TagInput input)
      82             :       throws RestApiException, IOException, PermissionBackendException, NoSuchProjectException {
      83           7 :     String ref = id.get();
      84           7 :     if (input == null) {
      85           0 :       input = new TagInput();
      86             :     }
      87           7 :     if (input.ref != null && !ref.equals(input.ref)) {
      88           1 :       throw new BadRequestException("ref must match URL");
      89             :     }
      90           7 :     if (input.revision != null) {
      91           2 :       input.revision = input.revision.trim();
      92             :     }
      93           7 :     if (Strings.isNullOrEmpty(input.revision)) {
      94           6 :       input.revision = Constants.HEAD;
      95             :     }
      96             : 
      97           7 :     ref = RefUtil.normalizeTagRef(ref);
      98           7 :     PermissionBackend.ForRef perm =
      99           7 :         permissionBackend.currentUser().project(resource.getNameKey()).ref(ref);
     100             : 
     101           7 :     try (Repository repo = repoManager.openRepository(resource.getNameKey())) {
     102           7 :       ObjectId revid = RefUtil.parseBaseRevision(repo, input.revision);
     103           7 :       RevWalk rw = RefUtil.verifyConnected(repo, revid);
     104             :       // Reachability through tags does not influence a commit's visibility, so no need to check for
     105             :       // visibility.
     106           7 :       RevObject object = rw.parseAny(revid);
     107           7 :       rw.reset();
     108           7 :       boolean isAnnotated = Strings.emptyToNull(input.message) != null;
     109           7 :       boolean isSigned = isAnnotated && input.message.contains("-----BEGIN PGP SIGNATURE-----\n");
     110           7 :       if (isSigned) {
     111           1 :         throw new MethodNotAllowedException("Cannot create signed tag \"" + ref + "\"");
     112           7 :       } else if (isAnnotated) {
     113           2 :         if (!check(perm, RefPermission.CREATE_TAG)) {
     114           1 :           throw new AuthException("Cannot create annotated tag \"" + ref + "\"");
     115             :         }
     116             : 
     117             :       } else {
     118           6 :         perm.check(RefPermission.CREATE);
     119             :       }
     120           7 :       if (repo.getRefDatabase().exactRef(ref) != null) {
     121           1 :         throw new ResourceConflictException("tag \"" + ref + "\" already exists");
     122             :       }
     123             : 
     124           7 :       try (Git git = new Git(repo)) {
     125           7 :         TagCommand tag =
     126           7 :             git.tag()
     127           7 :                 .setObjectId(object)
     128           7 :                 .setName(ref.substring(R_TAGS.length()))
     129           7 :                 .setAnnotated(isAnnotated)
     130           7 :                 .setSigned(isSigned);
     131             : 
     132           7 :         if (isAnnotated) {
     133           2 :           tag.setMessage(input.message)
     134           2 :               .setTagger(
     135             :                   resource
     136           2 :                       .getUser()
     137           2 :                       .asIdentifiedUser()
     138           2 :                       .newCommitterIdent(TimeUtil.now(), ZoneId.systemDefault()));
     139             :         }
     140             : 
     141           7 :         Ref result = tag.call();
     142           7 :         tagCache.updateFastForward(
     143           7 :             resource.getNameKey(), ref, ObjectId.zeroId(), result.getObjectId());
     144           7 :         referenceUpdated.fire(
     145           7 :             resource.getNameKey(),
     146             :             ref,
     147           7 :             ObjectId.zeroId(),
     148           7 :             result.getObjectId(),
     149           7 :             resource.getUser().asIdentifiedUser().state());
     150           7 :         try (RevWalk w = new RevWalk(repo)) {
     151           7 :           return Response.created(
     152           7 :               ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links));
     153             :         }
     154             :       }
     155           0 :     } catch (GitAPIException e) {
     156           0 :       logger.atSevere().withCause(e).log("Cannot create tag \"%s\"", ref);
     157           0 :       throw new IOException(e);
     158             :     }
     159             :   }
     160             : 
     161             :   private static boolean check(PermissionBackend.ForRef perm, RefPermission permission)
     162             :       throws PermissionBackendException {
     163             :     try {
     164           2 :       perm.check(permission);
     165           2 :       return true;
     166           1 :     } catch (AuthException e) {
     167           1 :       return false;
     168             :     }
     169             :   }
     170             : }

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