All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.scm.UserProvidedCredential Maven / Gradle / Ivy

There is a newer version: 2.3.11
Show newest version
/*
 * The MIT License
 *
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Fulvio Cavarretta,
 * Jean-Baptiste Quenot, Luca Domenico Milanesio, Renaud Bruyeron, Stephen Connolly,
 * Tom Huybrechts, Yahoo! Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.scm;

import hudson.model.AbstractProject;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.scm.SubversionSCM.DescriptorImpl.Credential;
import hudson.scm.SubversionSCM.DescriptorImpl.PasswordCredential;
import hudson.scm.SubversionSCM.DescriptorImpl.SshPublicKeyCredential;
import hudson.scm.SubversionSCM.DescriptorImpl.SslClientCertificateCredential;
import hudson.security.csrf.CrumbIssuer;
import hudson.util.IOException2;
import hudson.util.MultipartFormDataParser;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.logging.Logger;
import org.apache.commons.fileupload.FileItem;
import org.kohsuke.putty.PuTTYKey;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.StaplerRequest;
import org.tmatesoft.svn.core.SVNCancelException;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.auth.SVNAuthentication;
import org.tmatesoft.svn.core.auth.SVNUserNameAuthentication;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;

import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;

/**
 * Represents the SVN authentication credential given by the user via the <enterCredential> form fragment.
 * This is just a value object.
 *
 * @author Kohsuke Kawaguchi
 */
public class UserProvidedCredential implements Closeable {
    private final String username;
    private final String password;
    private final File keyFile;
    private final Boolean overrideGlobal;
    
    public Boolean getOverrideGlobal() {
    	return overrideGlobal;
    }
    /**
     * If non-null, this credential is submitted primarily to be used with this project.
     * This actually doesn't prevent Hudson from trying it with other projects.
     */
    public final AbstractProject inContextOf;

    /**
     * @deprecated as of 1.18
     *             Use {@link #UserProvidedCredential(String, String, File, AbstractProject)}
     */
    public UserProvidedCredential(String username, String password, File keyFile) {
        this(username, password, keyFile, null);
    }

    public UserProvidedCredential(String username, String password, File keyFile, AbstractProject inContextOf) {
        this(username, password, keyFile, Boolean.TRUE, inContextOf);
    }

    public UserProvidedCredential(String username, String password, File keyFile, Boolean overrideGlobal,
                                  AbstractProject inContextOf) {
        this.username = username;
        this.password = password;
        this.keyFile = keyFile;
        this.inContextOf = inContextOf;
        this.overrideGlobal = overrideGlobal;
    }

    /**
     * Parses the credential information from a form submission.
     */
    public static UserProvidedCredential fromForm(StaplerRequest req, MultipartFormDataParser parser)
        throws IOException {
        CrumbIssuer crumbIssuer = Hudson.getInstance().getCrumbIssuer();
        if (crumbIssuer != null && !crumbIssuer.validateCrumb(req, parser)) {
            throw HttpResponses.error(SC_FORBIDDEN, new IOException("No crumb found"));
        }

        String kind = parser.get("kind");
        int idx = Arrays.asList("", "password", "publickey", "certificate").indexOf(kind);

        String username = parser.get("username" + idx);
        String password = parser.get("password" + idx);
        Boolean overrideGlobal = Boolean.valueOf(parser.get("_.overrideGlobal"));

        // SVNKit wants a key in a file
        final File keyFile;
        final FileItem item;
        if (idx <= 1) {
            keyFile = null;
            item = null;
        } else {
            item = parser.getFileItem(kind.equals("publickey") ? "privateKey" : "certificate");
            keyFile = File.createTempFile("hudson", "key");
            if (item != null) {
                try {
                    item.write(keyFile);
                } catch (Exception e) {
                    throw new IOException2(e);
                }
                if (PuTTYKey.isPuTTYKeyFile(keyFile)) {
                    // TODO: we need a passphrase support
                    LOGGER.info("Converting " + keyFile + " from PuTTY format to OpenSSH format");
                    new PuTTYKey(keyFile, null).toOpenSSH(keyFile);
                }
            }
        }

        return new UserProvidedCredential(username, password, keyFile, overrideGlobal,
            req.findAncestorObject(AbstractProject.class)) {
            @Override
            public void close() throws IOException {
            	if (keyFile != null)
                    keyFile.delete();
                
                if (item != null)
                	item.delete();
            }
        };
    }

    public void close() throws IOException {
    }

    /**
     * {@link ISVNAuthenticationManager} that uses the user provided credential.
     */
    public class AuthenticationManagerImpl extends DefaultSVNAuthenticationManager {
        private Credential cred;
        
        private String realm;
        
        public String getRealm() {
        	return realm;
        }
        
        public Credential getCredential() {
        	return cred;
        }
        
        private final PrintWriter logWriter;

        /**
         * Set to true if SVNKit asked for a {@link SVNAuthentication}.
         * False indicates that the server didn't attempt to authenticate the client.
         */
        boolean authenticationAttempted;
        /**
         * Set to true if SVNKit acknowledged us whether the credential has worked or not.
         * I'm not sure when this won't happen, but presumably under some error conditions.
         */
        boolean authenticationAcknowledged;

        /**
         * Constructor for AuthenticationManager implementation.
         * Uses $Hudson.Home config directory for storing credentials.
         *
         * @param logWriter PrintWriter.
         * @see hudson.scm.SubversionSCM#getSubversionConfigDir().
         */
        public AuthenticationManagerImpl(PrintWriter logWriter) {
            this(SubversionSCM.getSubversionConfigDir(), logWriter);
        }

        public AuthenticationManagerImpl(File subversionConfigDir, PrintWriter logWriter) {
            super(subversionConfigDir, true, username, password, keyFile, password);
            this.logWriter = logWriter;
        }

        public AuthenticationManagerImpl(Writer w) {
            this(new PrintWriter(w));
        }

        public AuthenticationManagerImpl(TaskListener listener) {
            this(new PrintWriter(listener.getLogger(), true));
        }

        @Override
        public SVNAuthentication getFirstAuthentication(String kind, String realm, SVNURL url) throws SVNException {
        	
        	this.realm = realm;
        	
            authenticationAttempted = true;
            if (kind.equals(ISVNAuthenticationManager.USERNAME))
            // when using svn+ssh, svnkit first asks for ISVNAuthenticationManager.SSH
            // authentication to connect via SSH, then calls this method one more time
            // to get the user name. Perhaps svn takes user name on its own, separate
            // from OS user name? In any case, we need to return the same user name.
            // I don't set the cred field here, so that the 1st credential for ssh
            // won't get clobbered.
            {
                return new SVNUserNameAuthentication(username, false, null, false);
            }
            if (kind.equals(ISVNAuthenticationManager.PASSWORD)) {
                logWriter.println("Passing user name " + username + " and password you entered");
                cred = new PasswordCredential(username, password);
            }
            if (kind.equals(ISVNAuthenticationManager.SSH)) {
                if (keyFile == null) {
                    logWriter.println("Passing user name " + username + " and password you entered to SSH");
                    cred = new PasswordCredential(username, password);
                } else {
                    logWriter.println("Attempting a public key authentication with username " + username);
                    cred = new SshPublicKeyCredential(username, password, keyFile);
                }
            }
            if (kind.equals(ISVNAuthenticationManager.SSL)) {
                logWriter.println("Attempting an SSL client certificate authentcation");
                try {
                    cred = new SslClientCertificateCredential(keyFile, password);
                } catch (IOException e) {
                    e.printStackTrace(logWriter);
                    return null;
                }
            }

            if (cred == null) {
                logWriter.println("Unknown authentication method: " + kind);
                return null;
            }
            return cred.createSVNAuthentication(kind);
        }

        /**
         * Getting here means the authentication tried in {@link #getFirstAuthentication(String, String, SVNURL)}
         * didn't work.
         */
        @Override
        public SVNAuthentication getNextAuthentication(String kind, String realm, SVNURL url) throws SVNException {
            SVNErrorManager.authenticationFailed("Authentication failed for " + url, null);
            return null;
        }

        /**
         * This method has been deprecated as of hudson 2.2.0. This is for support for only old versions of svnkit where
         * overriding acknowledgeAuthentication needed to be overriden.
         * 
         */
        @Override
        @Deprecated 
        public void acknowledgeAuthentication(boolean accepted, String kind, String realm, SVNErrorMessage errorMessage,
                                              SVNAuthentication authentication) throws SVNException {
            authenticationAcknowledged = true;
            if (accepted) {
                assert cred != null;
                onSuccess(realm, cred,overrideGlobal);
            } else {
                logWriter.println("Failed to authenticate: " + errorMessage);
                if (errorMessage.getCause() != null) {
                    errorMessage.getCause().printStackTrace(logWriter);
                }
            }
            super.acknowledgeAuthentication(accepted, kind, realm, errorMessage, authentication);
        }

        /**
         * Called upon a successful acceptance of the credential.
         */
        protected void onSuccess(String realm, Credential cred, Boolean overrideGlobal) {
        }

        /**
         * Verifies that the expected authentication happened.
         */
        public void checkIfProtocolCompleted() throws SVNCancelException {
            if (!authenticationAttempted) {
                logWriter.println("No authentication was attempted.");
                throw new SVNCancelException();
            }
        }
    }

    private static final Logger LOGGER = Logger.getLogger(UserProvidedCredential.class.getName());
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy