001    /*
002     * Copyright (C) 2003-2009 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    package org.crsh.ssh.term;
020    
021    import org.apache.sshd.SshServer;
022    import org.apache.sshd.common.Session;
023    import org.apache.sshd.server.PasswordAuthenticator;
024    import org.apache.sshd.server.session.ServerSession;
025    import org.crsh.plugin.PluginContext;
026    import org.crsh.ssh.AuthenticationPlugin;
027    import org.crsh.ssh.term.scp.SCPCommandFactory;
028    import org.crsh.term.TermLifeCycle;
029    import org.crsh.term.spi.TermIOHandler;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    import java.net.URL;
034    import java.util.Iterator;
035    
036    /**
037     * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
038     * @version $Revision$
039     */
040    public class SSHLifeCycle extends TermLifeCycle {
041    
042      /** . */
043      public static final Session.AttributeKey<String> USERNAME = new Session.AttributeKey<java.lang.String>();
044    
045      /** . */
046      public static final Session.AttributeKey<String> PASSWORD = new Session.AttributeKey<java.lang.String>();
047    
048      /** . */
049      private final Logger log = LoggerFactory.getLogger(SSHLifeCycle.class);
050    
051      /** . */
052      private SshServer server;
053    
054      /** . */
055      private int port;
056    
057      /** . */
058      private URL keyURL;
059    
060      public SSHLifeCycle(PluginContext context) {
061        super(context);
062      }
063    
064      public int getPort() {
065        return port;
066      }
067    
068      public void setPort(int port) {
069        this.port = port;
070      }
071    
072      public URL getKeyURL() {
073        return keyURL;
074      }
075    
076      public void setKeyURL(URL keyURL) {
077        this.keyURL = keyURL;
078      }
079    
080      @Override
081      protected void doInit() {
082        try {
083    
084          //
085          TermIOHandler handler = getHandler();
086    
087          //
088          SshServer server = SshServer.setUpDefaultServer();
089          server.setPort(port);
090          server.setShellFactory(new CRaSHCommandFactory(handler));
091          server.setCommandFactory(new SCPCommandFactory(getContext()));
092          server.setKeyPairProvider(new URLKeyPairProvider(keyURL));
093    
094          AuthenticationPlugin plugin = null;
095          Iterator<AuthenticationPlugin> plugins = getContext().getPlugins(AuthenticationPlugin.class).iterator();
096          if (plugins.hasNext()) {
097            plugin = plugins.next();
098            if (plugins.hasNext()) {
099              throw new RuntimeException("More then one authentication plugin detected.");
100            }
101          }
102    
103          final AuthenticationPlugin authPlugin = plugin;
104          //
105          server.setPasswordAuthenticator(new PasswordAuthenticator() {
106            public boolean authenticate(String _username, String _password, ServerSession session) {
107              boolean auth = true;
108              if (authPlugin != null)
109              {
110                try {
111                  log.debug("Using authentication plugin " + authPlugin + " to authenticate user " + _username);
112                  auth = authPlugin.authenticate(_username, _password);
113                } catch (Exception e) {
114                  log.error("Exception authenticating user " + _username + " in authentication plugin: " + authPlugin, e);
115                  return false;
116                }
117              }
118              //TODO: I don't think we should store the password as an attribute in the server session for security reasons.
119    //          if (auth) {
120    //            session.setAttribute(USERNAME, _username);
121    //            session.setAttribute(PASSWORD, _password);
122    //          }
123    
124              return auth;
125            }
126          });
127    
128          //
129          log.info("About to start CRaSSHD");
130          server.start();
131          log.info("CRaSSHD started on port " + port);
132    
133          //
134          this.server = server;
135        }
136        catch (Throwable e) {
137          log.error("Could not start CRaSSHD", e);
138        }
139      }
140    
141      @Override
142      protected void doDestroy() {
143        if (server != null) {
144          try {
145            server.stop();
146          }
147          catch (InterruptedException e) {
148            log.debug("Got an interruption when stopping server", e);
149          }
150        }
151      }
152    }