LCOV - code coverage report
Current view: top level - server/git/validators - RefOperationValidators.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 63 67 94.0 %
Date: 2022-11-19 15:00:39 Functions: 9 9 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2014 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             : package com.google.gerrit.server.git.validators;
      15             : 
      16             : import static com.google.common.collect.ImmutableList.toImmutableList;
      17             : 
      18             : import com.google.common.collect.ImmutableList;
      19             : import com.google.common.collect.ImmutableListMultimap;
      20             : import com.google.common.flogger.FluentLogger;
      21             : import com.google.gerrit.entities.Account;
      22             : import com.google.gerrit.entities.Project;
      23             : import com.google.gerrit.entities.RefNames;
      24             : import com.google.gerrit.extensions.restapi.AuthException;
      25             : import com.google.gerrit.server.IdentifiedUser;
      26             : import com.google.gerrit.server.config.AllUsersName;
      27             : import com.google.gerrit.server.events.RefReceivedEvent;
      28             : import com.google.gerrit.server.permissions.GlobalPermission;
      29             : import com.google.gerrit.server.permissions.PermissionBackend;
      30             : import com.google.gerrit.server.permissions.PermissionBackendException;
      31             : import com.google.gerrit.server.plugincontext.PluginSetContext;
      32             : import com.google.gerrit.server.validators.ValidationException;
      33             : import com.google.inject.Inject;
      34             : import com.google.inject.assistedinject.Assisted;
      35             : import java.util.ArrayList;
      36             : import java.util.List;
      37             : import java.util.Locale;
      38             : import org.eclipse.jgit.lib.RefUpdate;
      39             : import org.eclipse.jgit.transport.ReceiveCommand;
      40             : 
      41             : /**
      42             :  * Collection of validation listeners that are called before a ref update is performed with the
      43             :  * command to be run. This is called from the git push path as well as Gerrit's handlers for
      44             :  * creating or deleting refs. Calls out to {@link RefOperationValidationListener} provided by
      45             :  * plugins.
      46             :  */
      47             : public class RefOperationValidators {
      48          59 :   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
      49             : 
      50             :   public interface Factory {
      51             :     RefOperationValidators create(
      52             :         Project project,
      53             :         IdentifiedUser user,
      54             :         ReceiveCommand cmd,
      55             :         ImmutableListMultimap<String, String> pushOptions);
      56             :   }
      57             : 
      58             :   public static ReceiveCommand getCommand(RefUpdate update, ReceiveCommand.Type type) {
      59          31 :     return new ReceiveCommand(
      60          31 :         update.getExpectedOldObjectId(), update.getNewObjectId(), update.getName(), type);
      61             :   }
      62             : 
      63             :   private final PermissionBackend.WithUser perm;
      64             :   private final AllUsersName allUsersName;
      65             :   private final PluginSetContext<RefOperationValidationListener> refOperationValidationListeners;
      66             :   private final RefReceivedEvent event;
      67             : 
      68             :   @Inject
      69             :   RefOperationValidators(
      70             :       PermissionBackend permissionBackend,
      71             :       AllUsersName allUsersName,
      72             :       PluginSetContext<RefOperationValidationListener> refOperationValidationListeners,
      73             :       @Assisted Project project,
      74             :       @Assisted IdentifiedUser user,
      75             :       @Assisted ReceiveCommand cmd,
      76          59 :       @Assisted ImmutableListMultimap<String, String> pushOptions) {
      77          59 :     this.perm = permissionBackend.user(user);
      78          59 :     this.allUsersName = allUsersName;
      79          59 :     this.refOperationValidationListeners = refOperationValidationListeners;
      80          59 :     event = new RefReceivedEvent();
      81          59 :     event.command = cmd;
      82          59 :     event.project = project;
      83          59 :     event.user = user;
      84          59 :     event.pushOptions = pushOptions;
      85          59 :   }
      86             : 
      87             :   /**
      88             :    * Returns informational validation messages and throws a {@link RefOperationValidationException}
      89             :    * when the first validator fails. Will not process any more validators after the first failure
      90             :    * was encountered.
      91             :    */
      92             :   public List<ValidationMessage> validateForRefOperation() throws RefOperationValidationException {
      93          59 :     List<ValidationMessage> messages = new ArrayList<>();
      94          59 :     boolean withException = false;
      95             :     try {
      96          59 :       messages.addAll(
      97             :           new DisallowCreationAndDeletionOfGerritMaintainedBranches(perm, allUsersName)
      98          59 :               .onRefOperation(event));
      99          59 :       refOperationValidationListeners.runEach(
     100           2 :           l -> messages.addAll(l.onRefOperation(event)), ValidationException.class);
     101           6 :     } catch (ValidationException e) {
     102           6 :       messages.add(new ValidationMessage(e.getMessage(), true));
     103           6 :       withException = true;
     104          59 :     }
     105             : 
     106          59 :     if (withException) {
     107           0 :       throwException(messages, event);
     108             :     }
     109             : 
     110          59 :     return messages;
     111             :   }
     112             : 
     113             :   private void throwException(List<ValidationMessage> messages, RefReceivedEvent event)
     114             :       throws RefOperationValidationException {
     115           6 :     String header =
     116           6 :         String.format(
     117             :             "Validation for %s of ref '%s' in project %s failed:",
     118           6 :             formatReceiveCommandType(event.command.getType()),
     119           6 :             event.command.getRefName(),
     120           6 :             event.project.getName());
     121           6 :     logger.atSevere().log("%s", header);
     122           6 :     throw new RefOperationValidationException(
     123           6 :         header, messages.stream().filter(ValidationMessage::isError).collect(toImmutableList()));
     124             :   }
     125             : 
     126             :   private static String formatReceiveCommandType(ReceiveCommand.Type type) {
     127           6 :     switch (type) {
     128             :       case CREATE:
     129           4 :         return "creation";
     130             :       case DELETE:
     131           4 :         return "deletion";
     132             :       case UPDATE:
     133           1 :         return "update";
     134             :       case UPDATE_NONFASTFORWARD:
     135           1 :         return "non-fast-forward update";
     136             :       default:
     137           0 :         return type.toString().toLowerCase(Locale.US);
     138             :     }
     139             :   }
     140             : 
     141             :   private static class DisallowCreationAndDeletionOfGerritMaintainedBranches
     142             :       implements RefOperationValidationListener {
     143             :     private final PermissionBackend.WithUser perm;
     144             :     private final AllUsersName allUsersName;
     145             : 
     146             :     DisallowCreationAndDeletionOfGerritMaintainedBranches(
     147          59 :         PermissionBackend.WithUser perm, AllUsersName allUsersName) {
     148          59 :       this.perm = perm;
     149          59 :       this.allUsersName = allUsersName;
     150          59 :     }
     151             : 
     152             :     @Override
     153             :     public List<ValidationMessage> onRefOperation(RefReceivedEvent refEvent)
     154             :         throws ValidationException {
     155          59 :       if (refEvent.project.getNameKey().equals(allUsersName)) {
     156           6 :         if (refEvent.command.getRefName().startsWith(RefNames.REFS_USERS)
     157           4 :             && !refEvent.command.getRefName().equals(RefNames.REFS_USERS_DEFAULT)) {
     158           4 :           if (refEvent.command.getType().equals(ReceiveCommand.Type.CREATE)) {
     159             :             try {
     160           1 :               if (!perm.test(GlobalPermission.ACCESS_DATABASE)) {
     161           1 :                 throw new ValidationException("Not allowed to create user branch.");
     162             :               }
     163           0 :             } catch (PermissionBackendException e) {
     164           0 :               throw new ValidationException("Not allowed to create user branch.", e);
     165           1 :             }
     166           1 :             if (Account.Id.fromRef(refEvent.command.getRefName()) == null) {
     167           1 :               throw new ValidationException(
     168           1 :                   String.format(
     169             :                       "Not allowed to create non-user branch under %s.", RefNames.REFS_USERS));
     170             :             }
     171           4 :           } else if (refEvent.command.getType().equals(ReceiveCommand.Type.DELETE)) {
     172             :             try {
     173           1 :               perm.check(GlobalPermission.ACCESS_DATABASE);
     174           2 :             } catch (AuthException | PermissionBackendException e) {
     175           2 :               throw new ValidationException("Not allowed to delete user branch.", e);
     176           1 :             }
     177             :           }
     178             :         }
     179             : 
     180           6 :         if (RefNames.isGroupRef(refEvent.command.getRefName())) {
     181           2 :           if (refEvent.command.getType().equals(ReceiveCommand.Type.CREATE)) {
     182           1 :             throw new ValidationException("Not allowed to create group branch.");
     183           2 :           } else if (refEvent.command.getType().equals(ReceiveCommand.Type.DELETE)) {
     184           2 :             throw new ValidationException("Not allowed to delete group branch.");
     185             :           }
     186             :         }
     187             :       }
     188          59 :       return ImmutableList.of();
     189             :     }
     190             :   }
     191             : }

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