Line data Source code
1 : // Copyright (C) 2008 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.sshd.commands; 16 : 17 : import com.google.common.collect.MultimapBuilder; 18 : import com.google.common.collect.SetMultimap; 19 : import com.google.common.flogger.FluentLogger; 20 : import com.google.gerrit.common.data.Capable; 21 : import com.google.gerrit.entities.Account; 22 : import com.google.gerrit.extensions.restapi.AuthException; 23 : import com.google.gerrit.server.CurrentUser; 24 : import com.google.gerrit.server.git.receive.AsyncReceiveCommits; 25 : import com.google.gerrit.server.notedb.ReviewerStateInternal; 26 : import com.google.gerrit.server.permissions.PermissionBackend; 27 : import com.google.gerrit.server.permissions.PermissionBackendException; 28 : import com.google.gerrit.server.permissions.ProjectPermission; 29 : import com.google.gerrit.sshd.AbstractGitCommand; 30 : import com.google.gerrit.sshd.CommandMetaData; 31 : import com.google.inject.Inject; 32 : import java.io.IOException; 33 : import org.eclipse.jgit.errors.TooLargeObjectInPackException; 34 : import org.eclipse.jgit.errors.UnpackException; 35 : import org.eclipse.jgit.transport.AdvertiseRefsHook; 36 : import org.eclipse.jgit.transport.ReceivePack; 37 : import org.kohsuke.args4j.Option; 38 : 39 : /** Receives change upload over SSH using the Git receive-pack protocol. */ 40 : @CommandMetaData( 41 : name = "receive-pack", 42 : description = "Standard Git server side command for client side git push") 43 3 : final class Receive extends AbstractGitCommand { 44 3 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 45 : 46 : @Inject private AsyncReceiveCommits.Factory factory; 47 : @Inject private PermissionBackend permissionBackend; 48 : 49 3 : private final SetMultimap<ReviewerStateInternal, Account.Id> reviewers = 50 3 : MultimapBuilder.hashKeys(2).hashSetValues().build(); 51 : 52 : @Option( 53 : name = "--reviewer", 54 : aliases = {"--re"}, 55 : metaVar = "EMAIL", 56 : usage = "request reviewer for change(s)") 57 : void addReviewer(Account.Id id) { 58 0 : reviewers.put(ReviewerStateInternal.REVIEWER, id); 59 0 : } 60 : 61 : @Option( 62 : name = "--cc", 63 : aliases = {}, 64 : metaVar = "EMAIL", 65 : usage = "CC user on change(s)") 66 : void addCC(Account.Id id) { 67 0 : reviewers.put(ReviewerStateInternal.CC, id); 68 0 : } 69 : 70 : @Override 71 : protected void runImpl() throws IOException, Failure { 72 2 : CurrentUser currentUser = session.getUser(); 73 : try { 74 2 : permissionBackend 75 2 : .user(currentUser) 76 2 : .project(project.getNameKey()) 77 2 : .check(ProjectPermission.RUN_RECEIVE_PACK); 78 0 : } catch (AuthException e) { 79 0 : throw new Failure(1, "fatal: receive-pack not permitted on this server", e); 80 0 : } catch (PermissionBackendException e) { 81 0 : throw new Failure(1, "fatal: unable to check permissions " + e); 82 2 : } 83 : 84 2 : AsyncReceiveCommits arc = 85 2 : factory.create(projectState, currentUser.asIdentifiedUser(), repo, null); 86 : 87 : try { 88 2 : Capable r = arc.canUpload(); 89 2 : if (r != Capable.OK) { 90 0 : throw die(r.getMessage()); 91 : } 92 0 : } catch (PermissionBackendException e) { 93 0 : throw die(e.getMessage()); 94 2 : } 95 : 96 2 : ReceivePack rp = arc.getReceivePack(); 97 : try { 98 2 : rp.receive(in, out, err); 99 2 : session.setPeerAgent(rp.getPeerUserAgent()); 100 0 : } catch (UnpackException badStream) { 101 : // In case this was caused by the user pushing an object whose size 102 : // is larger than the receive.maxObjectSizeLimit gerrit.config parameter 103 : // we want to present this error to the user 104 0 : if (badStream.getCause() instanceof TooLargeObjectInPackException) { 105 0 : StringBuilder msg = new StringBuilder(); 106 0 : msg.append("Receive error on project \"").append(projectState.getName()).append("\""); 107 0 : msg.append(" (user "); 108 0 : msg.append(currentUser.getUserName().orElse(null)); 109 0 : msg.append(" account "); 110 0 : msg.append(currentUser.getAccountId()); 111 0 : msg.append("): "); 112 0 : msg.append(badStream.getCause().getMessage()); 113 0 : logger.atInfo().log("%s", msg); 114 0 : throw new UnloggedFailure(128, "error: " + badStream.getCause().getMessage()); 115 : } 116 0 : StringBuilder msg = new StringBuilder(); 117 0 : msg.append("Unpack error on project \"").append(projectState.getName()).append("\":\n"); 118 : 119 0 : msg.append(" AdvertiseRefsHook: ").append(rp.getAdvertiseRefsHook()); 120 0 : if (rp.getAdvertiseRefsHook() == AdvertiseRefsHook.DEFAULT) { 121 0 : msg.append("DEFAULT"); 122 : } else { 123 0 : msg.append(rp.getAdvertiseRefsHook().getClass()); 124 : } 125 0 : msg.append("\n"); 126 : 127 0 : IOException detail = new IOException(msg.toString(), badStream); 128 0 : throw new Failure(128, "fatal: Unpack error, check server log", detail); 129 2 : } 130 2 : } 131 : }