Package com.samskivert.servlet.user

Source Code of com.samskivert.servlet.user.UserManager$PasswordAuthenticator

//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2012 Michael Bayne, et al.
// http://github.com/samskivert/samskivert/blob/master/COPYING

package com.samskivert.servlet.user;

import java.util.Properties;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.samskivert.io.PersistenceException;
import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.servlet.RedirectException;
import com.samskivert.servlet.util.CookieUtil;
import com.samskivert.servlet.util.RequestUtils;
import com.samskivert.util.Interval;
import com.samskivert.util.RunQueue;
import com.samskivert.util.StringUtil;
import com.samskivert.util.Tuple;

import static com.samskivert.servlet.Log.log;

/**
* The user manager provides easy access to user objects for servlets. It takes care of cookie
* management involved in login, logout and loading a user record during an authenticated session.
*/
public class UserManager
{
    /** An instance of the insecure authenticator for general-purpose use. */
    public static final Authenticator AUTH_INSECURE = new InsecureAuthenticator();

    /** An instance of the password authenticator for general-purpose use. */
    public static final Authenticator AUTH_PASSWORD = new PasswordAuthenticator();

    /**
     * A totally insecure authenticator that authenticates any user.  <em>Note:</em> Applications
     * that make use of this authenticator should make sure the user has already been authenticated
     * through some other means.
     */
    public static class InsecureAuthenticator implements Authenticator
    {
        // documentation inherited
        public void authenticateUser (User user, String username, Password password)
            throws InvalidPasswordException
        {
            // don't care
        }
    }

    /**
     * An authenticator that requires that the user-supplied password match the actual user
     * password.
     */
    public static class PasswordAuthenticator implements Authenticator
    {
        // documentation inherited
        public void authenticateUser (User user, String username, Password password)
            throws AuthenticationFailedException
        {
            if (!user.passwordsMatch(password)) {
                throw new InvalidPasswordException("error.invalid_password");
            }
        }
    }

    /**
     * Prepares this user manager for operation. Presently the user manager requires the
     * following configuration information:
     *
     * <ul>
     * <li><code>login_url</code>: Should be set to the URL to which to redirect a requester if
     * they are required to login before accessing the requested page. For example:
     *
     * <pre>
     * login_url = /usermgmt/login.ajsp?return=%R
     * </pre>
     *
     * The <code>%R</code> will be replaced with the URL encoded URL the user is currently
     * requesting (complete with query parameters) so that the login code can redirect the user
     * back to this request once they are authenticated.
     * </ul>
     *
     * @param config the user manager configuration properties.
     * @param conprov the database connection provider that will be used to obtain a connection to
     * the user database.
     */
    public void init (Properties config, ConnectionProvider conprov)
        throws PersistenceException
    {
        init(config, conprov, null);
    }

    /**
     * Prepares this user manager for operation. See {@link #init(Properties,ConnectionProvider)}.
     *
     * @param pruneQueue an optional run queue on which to run our periodic session pruning task.
     */
    public void init (Properties config, ConnectionProvider conprov, RunQueue pruneQueue)
        throws PersistenceException
    {
        // save this for later
        _config = config;

        // create the user repository
        _repository = createRepository(conprov);

        // fetch the login URL from the properties
        _loginURL = config.getProperty("login_url");
        if (_loginURL == null) {
            log.warning("No login_url supplied in user manager config. Authentication won't work.");
            _loginURL = "/missing_login_url";
        }

        // look up any override to our user auth cookie
        String authCook = config.getProperty("auth_cookie.name");
        if (!StringUtil.isBlank(authCook)) {
            _userAuthCookie = authCook;
        }

        if (USERMGR_DEBUG) {
            log.info("UserManager initialized", "acook", _userAuthCookie, "login", _loginURL);
        }

        // register a cron job to prune the session table every hour
        if (pruneQueue != null) {
            _pruner = new Interval(pruneQueue) {
                @Override public void expired () {
                    try {
                        _repository.pruneSessions();
                    } catch (PersistenceException pe) {
                        log.warning("Error pruning session table.", pe);
                    }
                }
            };
            _pruner.schedule(SESSION_PRUNE_INTERVAL, true);
        }
    }

    public void shutdown ()
    {
        // cancel our session table pruning thread
        if (_pruner != null) {
            _pruner.cancel();
        }
    }

    /**
     * Returns a reference to the repository in use by this user manager.
     */
    public UserRepository getRepository ()
    {
        return _repository;
    }

    /**
     * Fetches the necessary authentication information from the http request and loads the user
     * identified by that information.
     *
     * @return the user associated with the request or null if no user was associated with the
     * request or if the authentication information is bogus.
     */
    public User loadUser (HttpServletRequest req)
        throws PersistenceException
    {
        String authcook = CookieUtil.getCookieValue(req, _userAuthCookie);
        if (USERMGR_DEBUG) {
            log.info("Loading user by cookie", _userAuthCookie, authcook);
        }
        return loadUser(authcook);
    }

    /**
     * Loads up a user based on the supplied session authentication token.
     */
    public User loadUser (String authcode)
        throws PersistenceException
    {
        User user = (authcode == null) ? null : _repository.loadUserBySession(authcode);
        if (USERMGR_DEBUG) {
            log.info("Loaded user by authcode", "code", authcode, "user", user);
        }
        return user;
    }

    /**
     * Fetches the necessary authentication information from the http request and loads the user
     * identified by that information. If no user could be loaded (because the requester is not
     * authenticated), a redirect exception will be thrown to redirect the user to the login page
     * specified in the user manager configuration.
     *
     * @return the user associated with the request.
     */
    public User requireUser (HttpServletRequest req)
        throws PersistenceException, RedirectException
    {
        User user = loadUser(req);
        // if no user was loaded, we need to redirect these fine people to the login page
        if (user == null) {
            // first construct the redirect URL
            String eurl = RequestUtils.getLocationEncoded(req);
            String target = _loginURL.replace("%R", eurl);
            if (USERMGR_DEBUG) {
                log.info("No user found in require, redirecting", "to", target);
            }
            throw new RedirectException(target);
        }
        return user;
    }

    /**
     * Attempts to authenticate the requester and initiate an authenticated session for them. An
     * authenticated session involves their receiving a cookie that proves them to be authenticated
     * and an entry in the session database being created that maps their information to their
     * userid. If this call completes, the session was established and the proper cookies were set
     * in the supplied response object. If invalid authentication information is provided or some
     * other error occurs, an exception will be thrown.
     *
     * @param username The username supplied by the user.
     * @param password The password supplied by the user.
     * @param persist If true, the cookie will expire in one month, if false, the cookie will
     * expire at the end of the user's browser session.
     * @param req The request via which the login page was loaded.
     * @param rsp The response in which the cookie is to be set.
     * @param auth The authenticator used to check whether the user should be authenticated.
     *
     * @return the user object of the authenticated user.
     */
    public User login (String username, Password password, boolean persist,
                       HttpServletRequest req, HttpServletResponse rsp, Authenticator auth)
        throws PersistenceException, AuthenticationFailedException
    {
        // load up the requested user
        User user = _repository.loadUser(username);
        if (user == null) {
            throw new NoSuchUserException("error.no_such_user");
        }

        // run the user through the authentication gamut
        auth.authenticateUser(user, username, password);

        // give them the necessary cookies and business
        effectLogin(user, persist ? PERSIST_EXPIRE_DAYS : NON_PERSIST_EXPIRE_DAYS, req, rsp);

        return user;
    }

    /**
     * Attempts to authenticate the requester and initiate an authenticated session for them. A
     * session token will be assigned to the user and returned along with the associated {@link
     * User} record. It is assumed that the client will maintain the session token via its own
     * means.
     *
     * @param username the username supplied by the user.
     * @param password the password supplied by the user.
     * @param expires the number of days in which this session should expire.
     * @param auth the authenticator used to check whether the user should be authenticated.
     *
     * @return the user object of the authenticated user.
     */
    public Tuple<User,String> login (
        String username, Password password, int expires, Authenticator auth)
        throws PersistenceException, AuthenticationFailedException
    {
        // load up the requested user
        User user = _repository.loadUser(username);
        if (user == null) {
            throw new NoSuchUserException("error.no_such_user");
        }

        // run the user through the authentication gamut
        auth.authenticateUser(user, username, password);

        // register a session for this user
        String authcode = _repository.registerSession(user, expires);
        if (USERMGR_DEBUG) {
            log.info("Session started", "user", username, "code", authcode);
        }
        return new Tuple<User,String>(user, authcode);
    }

    /**
     * If a user is already known to be authenticated for one reason or other, this method can be
     * used to give them the appropriate authentication cookies to effect their login.
     *
     * @param expires the number of days in which to expire the session cookie, 0 means expire at
     * the end of the browser session.
     */
    public void effectLogin (
        User user, int expires, HttpServletRequest req, HttpServletResponse rsp)
        throws PersistenceException
    {
        String authcode = _repository.registerSession(user, Math.max(expires, 1));
        Cookie acookie = new Cookie(_userAuthCookie, authcode);
        // strip the hostname from the server and use that as the domain unless configured not to
        if (!"false".equalsIgnoreCase(_config.getProperty("auth_cookie.strip_hostname"))) {
            CookieUtil.widenDomain(req, acookie);
        }
        acookie.setPath("/");
        acookie.setMaxAge((expires > 0) ? (expires*24*60*60) : -1);
        if (USERMGR_DEBUG) {
            log.info("Setting cookie " + acookie + ".");
        }
        rsp.addCookie(acookie);
    }

    /**
     * Logs the user out.
     */
    public void logout (HttpServletRequest req, HttpServletResponse rsp)
    {
        // nothing to do if they don't already have an auth cookie
        String authcode = CookieUtil.getCookieValue(req, _userAuthCookie);
        if (authcode == null) {
            return;
        }

        // set them up the bomb
        Cookie c = new Cookie(_userAuthCookie, "x");
        c.setPath("/");
        c.setMaxAge(0);
        CookieUtil.widenDomain(req, c);
        if (USERMGR_DEBUG) {
            log.info("Clearing cookie " + c + ".");
        }
        rsp.addCookie(c);

        // we need an unwidened one to ensure that old-style cookies are wiped as well
        c = new Cookie(_userAuthCookie, "x");
        c.setPath("/");
        c.setMaxAge(0);
        rsp.addCookie(c);
    }

    /**
     * Validates that the supplied session key is still valid and if so, refreshes it for the
     * specified number of days.
     *
     * @return true if the session was located and refreshed, false otherwise.
     */
    public boolean refreshSession (String sessionKey, int expireDays)
        throws PersistenceException
    {
        return _repository.refreshSession(sessionKey, expireDays);
    }

    /**
     * Called by the user manager to create the user repository. Derived classes can override this
     * and create a specialized repository if they so desire.
     */
    protected UserRepository createRepository (ConnectionProvider conprov)
        throws PersistenceException
    {
        return new UserRepository(conprov);
    }

    /** Our user manager configuration. */
    protected Properties _config;

    /** The user repository. */
    protected UserRepository _repository;

    /** The interval for user session pruning. */
    protected Interval _pruner;

    /** The URL for the user login page. */
    protected String _loginURL;

    /** The name of our user authentication cookie. */
    protected String _userAuthCookie = USERAUTH_COOKIE;

    /** The user authentication cookie name. */
    protected static final String USERAUTH_COOKIE = "id_";

    /** Prune the session table every hour. */
    protected static final long SESSION_PRUNE_INTERVAL = 60L * 60L * 1000L;

    /** Indicates how long (in days) that a "persisting" session token should last. */
    protected static final int PERSIST_EXPIRE_DAYS = 30;

    /** Indicates how long (in days) that a "non-persisting" session token should last. */
    protected static final int NON_PERSIST_EXPIRE_DAYS = 1;

    /** Change this to true and recompile to debug cookie handling. */
    protected static final boolean USERMGR_DEBUG = false;
}
TOP

Related Classes of com.samskivert.servlet.user.UserManager$PasswordAuthenticator

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.