Line data Source code
1 : // Copyright (C) 2012 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; 16 : 17 : import com.google.common.base.Throwables; 18 : import com.google.common.util.concurrent.Atomics; 19 : import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission; 20 : import com.google.gerrit.extensions.restapi.AuthException; 21 : import com.google.gerrit.server.permissions.GlobalPermission; 22 : import com.google.gerrit.server.permissions.PermissionBackend; 23 : import com.google.gerrit.server.permissions.PermissionBackendException; 24 : import java.io.IOException; 25 : import java.util.ArrayDeque; 26 : import java.util.Map; 27 : import java.util.Set; 28 : import java.util.concurrent.atomic.AtomicReference; 29 : import org.apache.sshd.server.Environment; 30 : import org.apache.sshd.server.channel.ChannelSession; 31 : import org.apache.sshd.server.command.Command; 32 : 33 : /** Command that executes some other command. */ 34 : public class AliasCommand extends BaseCommand { 35 : private final DispatchCommandProvider root; 36 : private final PermissionBackend permissionBackend; 37 : private final CommandName command; 38 : private final AtomicReference<Command> atomicCmd; 39 : 40 : AliasCommand( 41 : @CommandName(Commands.ROOT) DispatchCommandProvider root, 42 : PermissionBackend permissionBackend, 43 0 : CommandName command) { 44 0 : this.root = root; 45 0 : this.permissionBackend = permissionBackend; 46 0 : this.command = command; 47 0 : this.atomicCmd = Atomics.newReference(); 48 0 : } 49 : 50 : @Override 51 : public void start(ChannelSession channel, Environment env) throws IOException { 52 : try { 53 0 : begin(channel, env); 54 0 : } catch (Failure e) { 55 0 : String msg = e.getMessage(); 56 0 : if (!msg.endsWith("\n")) { 57 0 : msg += "\n"; 58 : } 59 0 : err.write(msg.getBytes(ENC)); 60 0 : err.flush(); 61 0 : onExit(e.exitCode); 62 0 : } 63 0 : } 64 : 65 : private void begin(ChannelSession channel, Environment env) throws IOException, Failure { 66 0 : Map<String, CommandProvider> map = root.getMap(); 67 0 : for (String name : chain(command)) { 68 0 : CommandProvider p = map.get(name); 69 0 : if (p == null) { 70 0 : throw die(getName() + ": not found"); 71 : } 72 : 73 0 : Command cmd = p.getProvider().get(); 74 0 : if (!(cmd instanceof DispatchCommand)) { 75 0 : throw die(getName() + ": not found"); 76 : } 77 0 : map = ((DispatchCommand) cmd).getMap(); 78 0 : } 79 : 80 0 : CommandProvider p = map.get(command.value()); 81 0 : if (p == null) { 82 0 : throw die(getName() + ": not found"); 83 : } 84 : 85 0 : Command cmd = p.getProvider().get(); 86 0 : checkRequiresCapability(cmd); 87 0 : if (cmd instanceof BaseCommand) { 88 0 : BaseCommand bc = (BaseCommand) cmd; 89 0 : bc.setName(getName()); 90 0 : bc.setArguments(getArguments()); 91 : } 92 0 : provideStateTo(cmd); 93 0 : atomicCmd.set(cmd); 94 0 : cmd.start(channel, env); 95 0 : } 96 : 97 : @Override 98 : public void destroy(ChannelSession channel) { 99 0 : Command cmd = atomicCmd.getAndSet(null); 100 0 : if (cmd != null) { 101 : try { 102 0 : cmd.destroy(channel); 103 0 : } catch (Exception e) { 104 0 : Throwables.throwIfUnchecked(e); 105 0 : throw new RuntimeException(e); 106 0 : } 107 : } 108 0 : } 109 : 110 : private void checkRequiresCapability(Command cmd) throws Failure { 111 : try { 112 0 : Set<GlobalOrPluginPermission> check = GlobalPermission.fromAnnotation(cmd.getClass()); 113 : try { 114 0 : permissionBackend.currentUser().checkAny(check); 115 0 : } catch (AuthException err) { 116 0 : throw new UnloggedFailure(BaseCommand.STATUS_NOT_ADMIN, "fatal: " + err.getMessage()); 117 0 : } 118 0 : } catch (PermissionBackendException err) { 119 0 : throw new Failure(1, "fatal: permissions unavailable", err); 120 0 : } 121 0 : } 122 : 123 : private static Iterable<String> chain(CommandName command) { 124 0 : ArrayDeque<String> chain = new ArrayDeque<>(); 125 0 : while (command != null) { 126 0 : chain.addFirst(command.value()); 127 0 : command = Commands.parentOf(command); 128 : } 129 0 : chain.removeLast(); 130 0 : return chain; 131 : } 132 : }