AuthFactory.java 5.08 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
Matt Tucker's avatar
Matt Tucker committed
6
 * Copyright (C) 2004 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
7
 *
Matt Tucker's avatar
Matt Tucker committed
8 9
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
Matt Tucker's avatar
Matt Tucker committed
10 11 12 13
 */

package org.jivesoftware.messenger.auth;

14 15
import org.jivesoftware.util.*;
import org.jivesoftware.util.JiveGlobals;
Matt Tucker's avatar
Matt Tucker committed
16

Matt Tucker's avatar
Matt Tucker committed
17 18 19 20
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
Matt Tucker's avatar
Matt Tucker committed
21 22
 * Authentication service.
 *
Matt Tucker's avatar
Matt Tucker committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36
 * Users of Jive that wish to change the AuthProvider implementation used to authenticate users
 * can set the <code>AuthProvider.className</code> Jive property. For example, if
 * you have altered Jive to use LDAP for user information, you'd want to send a custom
 * implementation of AuthFactory to make LDAP authToken queries. After changing the
 * <code>AuthProvider.className</code> Jive property, you must restart your application
 * server.<p>
 * <p/>
 * The getAuthToken method that takes servlet request and response objects as arguments can be
 * used to implement single sign-on. Additionally, two helper methods are provided for securely
 * encrypting and decrypting login information so that it can be stored as a cookie value to
 * implement auto-login.<p>
 *
 * @author Matt Tucker
 */
Matt Tucker's avatar
Matt Tucker committed
37
public class AuthFactory {
Matt Tucker's avatar
Matt Tucker committed
38 39

    private static AuthProvider authProvider = null;
Matt Tucker's avatar
Matt Tucker committed
40
    private static MessageDigest digest;
Matt Tucker's avatar
Matt Tucker committed
41 42

    static {
Matt Tucker's avatar
Matt Tucker committed
43 44 45 46 47 48 49 50 51 52 53 54
        // Load an auth provider.
        String className = JiveGlobals.getXMLProperty("provider.auth.className",
                "org.jivesoftware.messenger.auth.DefaultAuthProvider");
        try {
            Class c = ClassUtils.forName(className);
            authProvider = (AuthProvider)c.newInstance();
        }
        catch (Exception e) {
            Log.error("Error loading auth provider: " + className, e);
            authProvider = new DefaultAuthProvider();
        }
        // Create a message digest instance.
Matt Tucker's avatar
Matt Tucker committed
55
        try {
Matt Tucker's avatar
Matt Tucker committed
56
            digest = MessageDigest.getInstance("SHA");
Matt Tucker's avatar
Matt Tucker committed
57 58 59 60 61 62 63
        }
        catch (NoSuchAlgorithmException e) {
            Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
        }
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
64 65 66
     * Returns true if the currently installed {@link AuthProvider} supports authentication
     * using plain-text passwords according to JEP-0078. Plain-text authentication is
     * not secure and should generally only be used over a TLS/SSL connection.
Matt Tucker's avatar
Matt Tucker committed
67
     *
Matt Tucker's avatar
Matt Tucker committed
68
     * @return true if plain text password authentication is supported.
Matt Tucker's avatar
Matt Tucker committed
69 70 71 72 73 74
     */
    public static boolean isPlainSupported() {
        return authProvider.isPlainSupported();
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
75 76
     * Returns true if the currently installed {@link AuthProvider} supports
     * digest authentication according to JEP-0078.
Matt Tucker's avatar
Matt Tucker committed
77
     *
Matt Tucker's avatar
Matt Tucker committed
78
     * @return true if digest authentication is supported.
Matt Tucker's avatar
Matt Tucker committed
79 80 81 82 83 84
     */
    public static boolean isDigestSupported() {
        return authProvider.isDigestSupported();
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
85 86 87
     * Authenticates a user with a username and plain text password and returns and
     * AuthToken. If the username and password do not match the record of
     * any user in the system, this method throws an UnauthorizedException.
Matt Tucker's avatar
Matt Tucker committed
88
     *
Matt Tucker's avatar
Matt Tucker committed
89 90
     * @param username the username.
     * @param password the password.
Matt Tucker's avatar
Matt Tucker committed
91 92 93
     * @return an AuthToken token if the username and password are correct.
     * @throws UnauthorizedException if the username and password do not match any existing user.
     */
Matt Tucker's avatar
Matt Tucker committed
94
    public static AuthToken authenticate(String username, String password)
95 96
            throws UnauthorizedException
    {
Matt Tucker's avatar
Matt Tucker committed
97
        authProvider.authenticate(username, password);
Matt Tucker's avatar
Matt Tucker committed
98
        return new AuthToken(username);
Matt Tucker's avatar
Matt Tucker committed
99 100 101
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
102 103 104 105
     * Authenticates a user with a username, token, and digest and returns an AuthToken.
     * The digest should be generated using the {@link #createDigest(String, String)} method.
     * If the username and digest do not match the record of any user in the system, the
     * method throws an UnauthorizedException.
Matt Tucker's avatar
Matt Tucker committed
106
     *
Matt Tucker's avatar
Matt Tucker committed
107 108 109 110 111 112 113
     * @param username the username.
     * @param token the token that was used with plain-text password to generate the digest.
     * @param digest the digest generated from plain-text password and unique token.
     * @return an AuthToken token if the username and digest are correct for the user's
     *      password and given token.
     * @throws UnauthorizedException if the username and password do not match any
     *      existing user.
Matt Tucker's avatar
Matt Tucker committed
114
     */
Matt Tucker's avatar
Matt Tucker committed
115 116 117
    public static AuthToken authenticate(String username, String token, String digest)
            throws UnauthorizedException
    {
Matt Tucker's avatar
Matt Tucker committed
118
        authProvider.authenticate(username, token, digest);
Matt Tucker's avatar
Matt Tucker committed
119
        return new AuthToken(username);
Matt Tucker's avatar
Matt Tucker committed
120 121 122
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
123
     * Returns a digest given a token and password, according to JEP-0078.
Matt Tucker's avatar
Matt Tucker committed
124 125 126
     *
     * @param token the token used in the digest.
     * @param password the plain-text password to be digested.
Matt Tucker's avatar
Matt Tucker committed
127
     * @return the digested result as a hex string.
Matt Tucker's avatar
Matt Tucker committed
128
     */
Matt Tucker's avatar
Matt Tucker committed
129 130 131 132 133
    public static String createDigest(String token, String password) {
        synchronized (digest) {
            digest.update(token.getBytes());
            return StringUtils.encodeHex(digest.digest(password.getBytes()));
        }
Matt Tucker's avatar
Matt Tucker committed
134 135
    }
}