Package hudson.scm

Source Code of hudson.scm.CredentialsSVNAuthenticationProviderImpl

package hudson.scm;

import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.CertificateCredentials;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.UsernameCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import hudson.model.Item;
import hudson.remoting.Channel;
import hudson.security.ACL;
import hudson.util.Scrambler;
import org.apache.commons.beanutils.PropertyUtils;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.*;

import javax.security.auth.DestroyFailedException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.logging.Logger;
import java.util.Map;
import java.util.Set;

/**
* @author stephenc
* @since 08/08/2013 12:15
*/
public class CredentialsSVNAuthenticationProviderImpl implements ISVNAuthenticationProvider, Serializable {

    private static final long serialVersionUID = 1L;

    private final SVNAuthenticationBuilderProvider provider;

    private final SVNUnauthenticatedRealmObserver realmObserver = new RemotableSVNUnauthenticatedRealmObserver();

    private static final SVNAuthentication ANONYMOUS = new SVNUserNameAuthentication("", false, null, false);

    public CredentialsSVNAuthenticationProviderImpl(Credentials credentials) {
        this.provider =
                new RemotableSVNAuthenticationBuilderProvider(credentials, Collections.<String, Credentials>emptyMap());
    }

    public CredentialsSVNAuthenticationProviderImpl(Credentials credentials,
                                                    Map<String, Credentials> credentialsByRealm) {
        this.provider = new RemotableSVNAuthenticationBuilderProvider(credentials,
                credentialsByRealm == null ? Collections.<String, Credentials>emptyMap() : credentialsByRealm);
    }

    public static CredentialsSVNAuthenticationProviderImpl createAuthenticationProvider(Item context, String remote, String credentialsId, Map<String,String> additionalCredentialIds) {
        StandardCredentials defaultCredentials;
        if (credentialsId == null) {
            defaultCredentials = null;
        } else {
            defaultCredentials = CredentialsMatchers
                    .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context,
                            ACL.SYSTEM, URIRequirementBuilder.fromUri(remote).build()),
                            CredentialsMatchers.allOf(idMatcher(credentialsId),
                                    CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(
                                            StandardCredentials.class), CredentialsMatchers.instanceOf(
                                            SSHUserPrivateKey.class))));
        }
        Map<String, Credentials> additional = new HashMap<String, Credentials>();
        if (additionalCredentialIds != null) {
            for (Map.Entry<String,String> c : additionalCredentialIds.entrySet()) {
                if (c.getValue() != null) {
                    StandardCredentials cred = CredentialsMatchers
                            .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context,
                                    ACL.SYSTEM, Collections.<DomainRequirement>emptyList()),
                                    CredentialsMatchers.allOf(idMatcher(c.getValue()),
                                            CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(
                                                    StandardCredentials.class), CredentialsMatchers.instanceOf(
                                                    SSHUserPrivateKey.class))));
                    if (cred != null) {
                        additional.put(c.getKey(), cred);
                    }
                }
            }
        }
        return new CredentialsSVNAuthenticationProviderImpl(defaultCredentials, additional);
    }

    public static CredentialsSVNAuthenticationProviderImpl createAuthenticationProvider(Item context, SubversionSCM scm,
                                                                                        SubversionSCM.ModuleLocation
                                                                                                location) {
        StandardCredentials defaultCredentials;
        if (location == null) {
            defaultCredentials = null;
        } else {
            defaultCredentials = CredentialsMatchers
                    .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context,
                            ACL.SYSTEM, URIRequirementBuilder.fromUri(location.remote).build()),
                            CredentialsMatchers.allOf(idMatcher(location.credentialsId),
                                    CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(
                                            StandardCredentials.class), CredentialsMatchers.instanceOf(
                                            SSHUserPrivateKey.class))));
        }
        Map<String, Credentials> additional = new HashMap<String, Credentials>();
        if (scm != null) {
            for (SubversionSCM.AdditionalCredentials c : scm.getAdditionalCredentials()) {
                if (c.getCredentialsId() != null) {
                    StandardCredentials cred = CredentialsMatchers
                            .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context,
                                    ACL.SYSTEM, Collections.<DomainRequirement>emptyList()),
                                    CredentialsMatchers.allOf(idMatcher(c.getCredentialsId()),
                                            CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(
                                                    StandardCredentials.class), CredentialsMatchers.instanceOf(
                                                    SSHUserPrivateKey.class))));
                    if (cred != null) {
                        additional.put(c.getRealm(), cred);
                    }
                }
            }
        }
        return new CredentialsSVNAuthenticationProviderImpl(defaultCredentials, additional);
    }

    private static CredentialsMatcher idMatcher(String credentialsId) {
        return credentialsId == null ? CredentialsMatchers.never() : CredentialsMatchers.withId(credentialsId);
    }

    public SVNAuthentication requestClientAuthentication(String kind, SVNURL url, String realm,
                                                         SVNErrorMessage errorMessage, SVNAuthentication previousAuth,
                                                         boolean authMayBeStored) {
        LOGGER.fine("Attempting auth for URL: " + url.toString() + "; Realm: " + realm);
        SVNAuthenticationBuilder builder = provider.getBuilder(realm);
        if (builder == null) {
            if (previousAuth == null && ISVNAuthenticationManager.USERNAME.equals(kind)) {
                return ANONYMOUS;
            }
            realmObserver.observe(realm);
            // finished all auth strategies, we are out of luck
            return null;
        }
        List<SVNAuthentication> authentications = builder.build(kind, url);
        int index = previousAuth == null ? 0 : indexOf(authentications, previousAuth) + 1;
        if (index >= authentications.size()) {
            if (previousAuth == null && ISVNAuthenticationManager.USERNAME.equals(kind)) {
                return ANONYMOUS;
            }
            realmObserver.observe(realm);
            return null;
        }
        return authentications.get(index);
    }

    public void resetUnauthenticatedRealms() {
        realmObserver.reset();
    }

    public Set<String> getUnauthenticatedRealms() {
        return realmObserver.get();
    }

    private static int indexOf(List<SVNAuthentication> list, SVNAuthentication o) {
        int index = 0;
        for (SVNAuthentication v : list) {
            if (equals(v, o)) {
                return index;
            }
            index++;
        }
        return -1;
    }

    private static boolean equals(SVNAuthentication a1, SVNAuthentication a2) {
        if (a1 == null && a2 == null) {
            return true;
        }
        if (a1 == null || a2 == null) {
            return false;
        }
        if (a1.getClass() != a2.getClass()) {
            return false;
        }

        try {
            return describeBean(a1).equals(describeBean(a2));
        } catch (IllegalAccessException e) {
            return false;
        } catch (InvocationTargetException e) {
            return false;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    /**
     * In preparation for a comparison, char[] needs to be converted that supports value equality.
     */
    @SuppressWarnings("unchecked")
    private static Map describeBean(Object o)
            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        Map<?, ?> m = PropertyUtils.describe(o);
        for (Map.Entry e : m.entrySet()) {
            Object v = e.getValue();
            if (v instanceof char[]) {
                char[] chars = (char[]) v;
                e.setValue(new String(chars));
            }
        }
        return m;
    }


    public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) {
        return ACCEPTED_TEMPORARY;
    }

    public static interface SVNUnauthenticatedRealmObserver extends Serializable {
        void observe(String realm);

        void reset();

        Set<String> get();
    }

    public static class RemotableSVNUnauthenticatedRealmObserver implements SVNUnauthenticatedRealmObserver {

        private final Set<String> realms = new LinkedHashSet<String>();

        /**
         * When sent to the remote node, send a proxy.
         */
        private Object writeReplace() {
            return Channel.current().export(SVNUnauthenticatedRealmObserver.class, this);
        }

        public void observe(String realm) {
            synchronized (realms) {
                realms.add(realm);
            }
        }

        public void reset() {
            synchronized (realms) {
                realms.clear();
            }
        }

        public Set<String> get() {
            synchronized (realms) {
                return new LinkedHashSet<String>(realms);
            }
        }
    }

    public static interface SVNAuthenticationBuilderProvider extends Serializable {
        SVNAuthenticationBuilder getBuilder(String realm);
    }

    public static class RemotableSVNAuthenticationBuilderProvider implements SVNAuthenticationBuilderProvider {

        private static final long serialVersionUID = 1L;

        private final Credentials defaultCredentials;
        private final Map<String, Credentials> credentialsByRealm;

        public RemotableSVNAuthenticationBuilderProvider(Credentials defaultCredentials,
                                                         Map<String, Credentials> credentialsByRealm) {
            this.defaultCredentials = defaultCredentials;
            this.credentialsByRealm = credentialsByRealm;
        }

        /**
         * When sent to the remote node, send a proxy.
         */
        private Object writeReplace() {
            return Channel.current().export(SVNAuthenticationBuilderProvider.class, this);
        }

        public SVNAuthenticationBuilder getBuilder(String realm) {
            Credentials c = credentialsByRealm.get(realm);
            if (c == null) {
                c = defaultCredentials;
            }
            if (c instanceof CertificateCredentials) {
                return new SVNCertificateAuthenticationBuilder((CertificateCredentials) c);
            }
            if (c instanceof SSHUserPrivateKey) {
                return new SVNUsernamePrivateKeysAuthenticationBuilder(
                        (SSHUserPrivateKey) c);
            }
            if (c instanceof UsernamePasswordCredentials) {
                return new SVNUsernamePasswordAuthenticationBuilder((UsernamePasswordCredentials) c);
            }
            if (c instanceof UsernameCredentials) {
                return new SVNUsernameAuthenticationBuilder((UsernameCredentials) c);
            }
            return new SVNEmptyAuthenticationBuilder();
        }
    }

    public static interface SVNAuthenticationBuilder extends Serializable {
        List<SVNAuthentication> build(String kind, SVNURL url);
    }

    public static class SVNEmptyAuthenticationBuilder implements SVNAuthenticationBuilder {

        private static final long serialVersionUID = 1L;

        public List<SVNAuthentication> build(String kind, SVNURL url) {
            return Collections.emptyList();
        }
    }

    public static class SVNUsernameAuthenticationBuilder implements SVNAuthenticationBuilder {

        private static final long serialVersionUID = 1L;

        private final String username;

        public SVNUsernameAuthenticationBuilder(UsernameCredentials c) {
            this.username = c.getUsername();
        }

        public List<SVNAuthentication> build(String kind, SVNURL url) {
            if (ISVNAuthenticationManager.USERNAME.equals(kind)) {
                return Collections.<SVNAuthentication>singletonList(
                        new SVNUserNameAuthentication(username, true, url, true));
            }
            return Collections.emptyList();
        }
    }

    public static class SVNUsernamePasswordAuthenticationBuilder implements SVNAuthenticationBuilder {

        private static final long serialVersionUID = 1L;

        private final String username;
        private final String password;

        public SVNUsernamePasswordAuthenticationBuilder(UsernamePasswordCredentials c) {
            this.username = c.getUsername();
            this.password = Scrambler.scramble(c.getPassword().getPlainText());
        }

        public List<SVNAuthentication> build(String kind, SVNURL url) {
            if (ISVNAuthenticationManager.PASSWORD.equals(kind)) {
                return Collections.<SVNAuthentication>singletonList(
                        new SVNPasswordAuthentication(username, Scrambler.descramble(password), false, url, false));
            }
            if (ISVNAuthenticationManager.SSH.equals(kind)) {
                return Collections.<SVNAuthentication>singletonList(
                        new SVNSSHAuthentication(username, Scrambler.descramble(password), -1, false, url, false));
            }
            return Collections.emptyList();
        }
    }

    public static class SVNUsernamePrivateKeysAuthenticationBuilder implements SVNAuthenticationBuilder {
        private static final long serialVersionUID = 1L;

        private final String username;
        private final String passphrase;
        private final List<String> privateKeys;

        public SVNUsernamePrivateKeysAuthenticationBuilder(SSHUserPrivateKey c) {
            username = c.getUsername();
            passphrase = Scrambler.scramble(c.getPassphrase().getPlainText());
            privateKeys = new ArrayList<String>(c.getPrivateKeys());
        }

        public List<SVNAuthentication> build(String kind, SVNURL url) {
            List<SVNAuthentication> result = new ArrayList<SVNAuthentication>();
            if (ISVNAuthenticationManager.SSH.equals(kind)) {
                for (String privateKey : privateKeys) {
                    result.add(new SVNSSHAuthentication(username, privateKey.toCharArray(),
                            Scrambler.descramble(passphrase), -1, false, url, false));
                }
            }
            return result;
        }
    }

    public static class SVNCertificateAuthenticationBuilder implements SVNAuthenticationBuilder {
        private static final long serialVersionUID = 1L;

        private final byte[] certificateFile;
        private final String password;

        public SVNCertificateAuthenticationBuilder(CertificateCredentials c) {
            String password = c.getPassword().getPlainText();
            this.password = Scrambler.scramble(password);
            char[] passwordChars = password.toCharArray();
            KeyStore.PasswordProtection passwordProtection =
                    new KeyStore.PasswordProtection(passwordChars);
            try {
                // ensure we map the keystore to the correct type
                KeyStore dst = KeyStore.getInstance("PKCS12");
                dst.load(null, null);
                KeyStore src = c.getKeyStore();
                for (Enumeration<String> e = src.aliases(); e.hasMoreElements(); ) {
                    String alias = e.nextElement();
                    KeyStore.Entry entry;
                    try {
                        entry = src.getEntry(alias, null);
                    } catch (UnrecoverableEntryException e1) {
                        try {
                            entry = src.getEntry(alias, passwordProtection);
                        } catch (UnrecoverableEntryException e2) {
                            throw new RuntimeException(
                                    SVNErrorMessage
                                            .create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "Unable to save certificate").getFullMessage(),
                                            e2);
                        }
                    }
                    dst.setEntry(alias, entry, passwordProtection);
                }
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                dst.store(bos, passwordChars);
                certificateFile = bos.toByteArray();
            } catch (KeyStoreException e) {
                throw new RuntimeException(
                        SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "Unable to save certificate").getFullMessage(),
                                e);
            } catch (CertificateException e) {
                throw new RuntimeException(
                        SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "Unable to save certificate").getFullMessage(),
                                e);
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(
                        SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "Unable to save certificate").getFullMessage(),
                                e);
            } catch (IOException e) {
                throw new RuntimeException(
                        SVNErrorMessage.create(SVNErrorCode.AUTHN_CREDS_UNAVAILABLE, "Unable to save certificate").getFullMessage(),
                                e);
            } finally {
                try {
                    passwordProtection.destroy();
                } catch (DestroyFailedException e) {
                    // ignore
                }
                Arrays.fill(passwordChars, ' ');
            }
        }

        public List<SVNAuthentication> build(String kind, SVNURL url) {
            if (ISVNAuthenticationManager.SSL.equals(kind)) {
                SVNSSLAuthentication authentication =
                        new SVNSSLAuthentication(String.valueOf(certificateFile), Scrambler.descramble(password), false, url, false);
                authentication.setCertificatePath("dummy"); // TODO: remove this JENKINS-19175 workaround
                return Collections.<SVNAuthentication>singletonList(
                        authentication);
            }
            return Collections.emptyList();
        }
    }

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

}
TOP

Related Classes of hudson.scm.CredentialsSVNAuthenticationProviderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.