ClientAuthenticationCache.java 4.1 KB
Newer Older
1 2 3 4 5
package hudson.cli;

import hudson.FilePath;
import hudson.remoting.Channel;
import hudson.util.Secret;
K
Kohsuke Kawaguchi 已提交
6
import jenkins.model.Jenkins;
7
import jenkins.security.MasterToSlaveCallable;
8 9 10 11 12 13 14 15
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.UserDetails;
import org.springframework.dao.DataAccessException;

import java.io.File;
import java.io.IOException;
16
import java.io.InputStream;
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Properties;

/**
 * Represents the authentication credential store of the CLI client.
 *
 * <p>
 * This object encapsulates a remote manipulation of the credential store.
 * We store encrypted user names.
 *
 * @author Kohsuke Kawaguchi
 * @since 1.351
 */
public class ClientAuthenticationCache implements Serializable {
    /**
     * Where the store should be placed.
     */
    private final FilePath store;

    /**
     * Loaded contents of the store.
     */
    private final Properties props = new Properties();

    public ClientAuthenticationCache(Channel channel) throws IOException, InterruptedException {
43
        store = (channel==null ? FilePath.localChannel :  channel).call(new MasterToSlaveCallable<FilePath, IOException>() {
44 45
            public FilePath call() throws IOException {
                File home = new File(System.getProperty("user.home"));
46 47 48 49 50
                File hudsonHome = new File(home, ".hudson");
                if (hudsonHome.exists()) {
                    return new FilePath(new File(hudsonHome, "cli-credentials"));
                }
                return new FilePath(new File(home, ".jenkins/cli-credentials"));
51 52 53
            }
        });
        if (store.exists()) {
54 55 56 57 58 59
            InputStream istream = store.read();
            try {
                props.load(istream);
            } finally {
                istream.close();
            }
60 61 62 63
        }
    }

    /**
64
     * Gets the persisted authentication for this Jenkins.
65
     *
66
     * @return {@link jenkins.model.Jenkins#ANONYMOUS} if no such credential is found, or if the stored credential is invalid.
67 68
     */
    public Authentication get() {
69
        Jenkins h = Jenkins.getActiveInstance();
70
        Secret userName = Secret.decrypt(props.getProperty(getPropertyKey()));
71
        if (userName==null) return Jenkins.ANONYMOUS; // failed to decrypt
72
        try {
73
            UserDetails u = h.getSecurityRealm().loadUserByUsername(userName.getPlainText());
74
            return new UsernamePasswordAuthenticationToken(u.getUsername(), "", u.getAuthorities());
75
        } catch (AuthenticationException e) {
76
            return Jenkins.ANONYMOUS;
77
        } catch (DataAccessException e) {
78
            return Jenkins.ANONYMOUS;
79 80 81 82 83 84 85
        }
    }

    /**
     * Computes the key that identifies this Hudson among other Hudsons that the user has a credential for.
     */
    private String getPropertyKey() {
86
        String url = Jenkins.getActiveInstance().getRootUrl();
87 88 89 90 91 92 93 94
        if (url!=null)  return url;
        return Secret.fromString("key").toString();
    }

    /**
     * Persists the specified authentication.
     */
    public void set(Authentication a) throws IOException, InterruptedException {
95
        Jenkins h = Jenkins.getActiveInstance();
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

        // make sure that this security realm is capable of retrieving the authentication by name,
        // as it's not required.
        UserDetails u = h.getSecurityRealm().loadUserByUsername(a.getName());
        props.setProperty(getPropertyKey(), Secret.fromString(u.getUsername()).getEncryptedValue());

        save();
    }

    /**
     * Removes the persisted credential, if there's one.
     */
    public void remove() throws IOException, InterruptedException {
        if (props.remove(getPropertyKey())!=null)
            save();
    }

    private void save() throws IOException, InterruptedException {
114 115 116 117 118 119 120 121
        OutputStream os = store.write();
        try {
            props.store(os,"Credential store");
        } finally {
            os.close();
        }
        // try to protect this file from other users, if we can.
        store.chmod(0600);
122 123
    }
}