com.redhat.rhn.manager.session.SessionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rhn.manager.session.SessionManager.java

Source

/**
 * Copyright (c) 2009--2014 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */
package com.redhat.rhn.manager.session;

import com.redhat.rhn.common.conf.Config;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.hibernate.LookupException;
import com.redhat.rhn.common.security.HMAC;
import com.redhat.rhn.common.util.TimeUtils;
import com.redhat.rhn.domain.session.InvalidSessionIdException;
import com.redhat.rhn.domain.session.WebSession;
import com.redhat.rhn.domain.session.WebSessionFactory;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.manager.BaseManager;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;

/**
 * SessionManager is the helper class used to fetch configuration
 * about com.redhat.rhn.domain.session.Session objects.
 * @version $Rev$
 */
public class SessionManager extends BaseManager {

    /**
     * Logger for this class
     */
    private static Logger logger = Logger.getLogger(SessionManager.class);

    public static final String SEC_PARM_TOKENIZER_CHAR = ":";
    // Timeout value 900,000 = 15 min
    public static final long TIMEOUT_VAL = 900000;

    protected SessionManager() {
    }

    /**
     * Return the lifetime in seconds of the Session.  This differs
     * from the timeoutValue() above which is the time from now
     * the Session should finish. It's a subtle difference but
     * needed since the PXTSession table expects the time the session
     * should expire while the cookie in Tomcat expects the lifetime
     * of the session in seconds, it will calculate the time for you.
     * @return the lifetime in seconds of the Session.
     */
    public static long lifetimeValue() {
        return Long.parseLong(Config.get().getString(ConfigDefaults.WEB_SESSION_DATABASE_LIFETIME));
    }

    /**
     * Create a new Session from scratch with the specified attributes.
     * This session will already be in the database, and will thus have
     * a session ID.
     * @param uid User Id associated with this WebSession
     * @param duration duration of WebSession, in ms.
     * @return the Session created
     */
    public static WebSession makeSession(Long uid, long duration) {
        WebSession s = WebSessionFactory.createSession();
        if (uid != null) {
            s.setWebUserId(uid);
        } else {
            s.setWebUserId(null);
        }

        s.setExpires(TimeUtils.currentTimeSeconds() + duration);
        WebSessionFactory.save(s);
        return s;
    }

    /**
     * Removes the given session.
     * @param s WebSession to remove.
     * @return number of sessions removed (typically 1 or 0).
     */
    public static int removeSession(WebSession s) {
        return WebSessionFactory.remove(s);
    }

    /**
     * Returns the session identified by sessionKey
     * @param sessionKey The key for the session that is requested
     * @return Returns the WebSession identified by the sessionKey
     */
    public static WebSession loadSession(String sessionKey) {
        return SessionManager.lookupByKey(sessionKey);
    }

    /**
     * Removes the session specified by sessionKey from the database.
     * @param sessionKey Key for the session you want to remove.
     */
    public static void killSession(String sessionKey) {
        WebSession session = loadSession(sessionKey);
        removeSession(session);
    }

    /**
     * Generates a session key for usage in passing sensitive
     * url based parameters around in a safe way.
     *
     * @param data String data to generate key on
     * @return String SHA-256 hash (with "salt") of passed in data.
     */
    public static String generateSessionKey(String data) {
        Config c = Config.get();
        MessageDigest msgDigest = null;

        try {
            msgDigest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException nsae) {
            // this really shouldn't happen.  really.
            throw new IllegalArgumentException("Unable to instantiate SHA-256 " + "MessageDigest object");
        }

        msgDigest.update(c.getString(ConfigDefaults.WEB_SESSION_SECRET_1).getBytes());
        msgDigest.update(":".getBytes());
        msgDigest.update(c.getString(ConfigDefaults.WEB_SESSION_SECRET_2).getBytes());
        msgDigest.update(":".getBytes());
        msgDigest.update(data.getBytes());
        msgDigest.update(":".getBytes());
        msgDigest.update(c.getString(ConfigDefaults.WEB_SESSION_SECRET_3).getBytes());
        msgDigest.update(":".getBytes());
        msgDigest.update(c.getString(ConfigDefaults.WEB_SESSION_SECRET_4).getBytes());

        return HMAC.byteArrayToHex(msgDigest.digest());
    }

    /**
     * Create a secure param string without timestamp
     *
     * @param data param to be secured
     * @return String secure param string (no timestamp)
     */
    public static String makeSecureParamNoTimestamp(String data) {
        String secparm = "";
        if (data != null) {
            secparm = data + SEC_PARM_TOKENIZER_CHAR + generateSessionKey(data);
        }
        return secparm;
    }

    /**
     * Create a secure param string with a timestamp for the current time.
     *
     * @param data param to be secured.
     * @return String secure param string (timestamped)
     */
    public static String makeSecureParamTimestamped(String data) {
        String secparm = "";
        if (data != null) {
            secparm = data + SEC_PARM_TOKENIZER_CHAR + Calendar.getInstance().getTimeInMillis();
            secparm = secparm + SEC_PARM_TOKENIZER_CHAR + generateSessionKey(secparm);
        }
        return secparm;
    }

    /**
     * Determine if this is a secure param as created by the other
     * methods on this class. The string parameter "data" should
     * be of the form <param>:<encoded> or <param>:<timestamp>:<encoded>.
     * If it is of the 1st form, then the <param> value is re-encoded
     * and it is compared to the <encoded> value. If it is of the 2nd
     * form, then first the timeout value is compared against the
     * <timestamp> value. If that is in range, then the <param>:<timestamp>
     * combined value is re-encoded and compared against the <encoded>
     * value.
     *
     * @param data potentially secure param string
     * @return boolean true if it is, otherwise false
     */
    public static boolean isValidSecureParam(String data) {
        if (logger.isDebugEnabled()) {
            logger.debug("isValidSecureParam(String data=" + data + ") - start");
        }

        if (data != null && !data.equals("") && data.indexOf(SEC_PARM_TOKENIZER_CHAR) > 0) {
            String[] vals = StringUtils.split(data, SEC_PARM_TOKENIZER_CHAR);
            if (isNonTimestampedParamString(vals)) {
                boolean returnboolean = isValidNonTimestampedParamString(vals);
                if (logger.isDebugEnabled()) {
                    logger.debug("isValidSecureParam(String)" + " - end 1 - return value=" + returnboolean);
                }
                return returnboolean;
            } else if (isTimestampedParamString(vals)) {
                boolean returnboolean = isValidTimestampedParamString(vals);
                if (logger.isDebugEnabled()) {
                    logger.debug("isValidSecureParam(String) - end 2 - return value=" + returnboolean);
                }
                return returnboolean;
            }
        }

        if (logger.isDebugEnabled()) {
            logger.debug("isValidSecureParam(String) - end 3 - return value=" + false);
        }
        return false;
    }

    /**
     * If this is a valid secure param string, then extract the secure
     * param and return it. Otherwise return an empty string.
     *
     * @param data secure param string
     * @return String secure param or empty string
     */
    public static String extractSecureParam(String data) {
        if (logger.isDebugEnabled()) {
            logger.debug("extractSecureParam(String data=" + data + ") - start");
        }

        String parm = "";

        if (isValidSecureParam(data)) {
            parm = data.substring(0, data.indexOf(SEC_PARM_TOKENIZER_CHAR));
        }

        if (logger.isDebugEnabled()) {
            logger.debug("extractSecureParam(String) - end - return value=" + parm);
        }
        return parm;
    }

    private static boolean isTimestampedParamString(String[] vals) {
        if (logger.isDebugEnabled()) {
            logger.debug("isTimestampedParamString(String[] vals=" + vals + ") - start");
        }

        boolean returnboolean = vals.length == 3;
        if (logger.isDebugEnabled()) {
            logger.debug("isTimestampedParamString(String[]) - end - return value=" + returnboolean);
        }
        return returnboolean;
    }

    private static boolean isNonTimestampedParamString(String[] vals) {
        if (logger.isDebugEnabled()) {
            logger.debug("isNonTimestampedParamString(String[] vals=" + vals + ") - start");
        }

        boolean returnboolean = vals.length == 2;
        if (logger.isDebugEnabled()) {
            logger.debug("isNonTimestampedParamString(String[]) - end - return value=" + returnboolean);
        }
        return returnboolean;
    }

    private static boolean isValidTimestampedParamString(String[] vals) {
        if (logger.isDebugEnabled()) {
            logger.debug("isValidTimestampedParamString(String[] vals=" + vals + ") - start");
        }

        String newEncoded = generateSessionKey(vals[0] + SEC_PARM_TOKENIZER_CHAR + vals[1]);
        long currTime = Calendar.getInstance().getTimeInMillis();
        long timeStamped = Long.parseLong(vals[1]);
        if ((currTime - timeStamped) <= TIMEOUT_VAL && newEncoded.equals(vals[2])) {
            if (logger.isDebugEnabled()) {
                logger.debug("isValidTimestampedParamString(String[])" + " - end 1 - return value=" + true);
            }
            return true;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("isValidTimestampedParamString(String[])" + " - end 2 - return value=" + false);
        }
        return false;
    }

    private static boolean isValidNonTimestampedParamString(String[] vals) {
        if (logger.isDebugEnabled()) {
            logger.debug("isValidNonTimestampedParamString(String[] vals=" + vals + ") - start");
        }

        String newEncoded = generateSessionKey(vals[0]);
        if (newEncoded.equals(vals[1])) {
            if (logger.isDebugEnabled()) {
                logger.debug("isValidNonTimestampedParamString(String[])" + " - end 1 - return value=" + true);
            }
            return true;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("isValidNonTimestampedParamString(String[])" + " - end -  2 return value=" + false);
        }
        return false;
    }

    /**
     * Verifies that the specified string is a valid pxt session key.
     *
     * @param key The session key to be validated
     *
     * @return <code>true</code> if the key is valid. Note that <code>null</code> is
     * acceptable input and will result in <code>false</code> being returned.
     */
    public static boolean isPxtSessionKeyValid(String key) {
        String[] data = StringUtils.split(key, 'x');

        if (data != null && data.length == 2) {
            String recomputedkey = generateSessionKey(data[0]);
            logger.debug("recomputed [" + recomputedkey + "] cookiekey [" + data[1] + "]");
            return recomputedkey.equals(data[1]);
        }

        return false;
    }

    /**
     * Lookup a Session by it's key
     * @param key The key containing the session id and hash
     * @return Returns the session if the key is valid.
     */
    public static WebSession lookupByKey(String key) {
        //Make sure we didn't get null for a key
        if (key == null || key.equals("")) {
            throw new InvalidSessionIdException("Session key cannot be empty null.");
        }

        //Get the id
        String[] keyParts = StringUtils.split(key, 'x');

        //make sure the id is numeric and can be made into a Long
        if (!StringUtils.isNumeric(keyParts[0])) {
            throw new InvalidSessionIdException(
                    "Session id: " + keyParts[0] + " is not valid. Session ids must be numeric.");
        }

        //Load the session
        Long sessionId = new Long(keyParts[0]);
        WebSession session = WebSessionFactory.lookupById(sessionId);

        //Make sure we found a session
        if (session == null) {
            throw new LookupException("Could not find session with id: " + sessionId);
        }

        //Verify the key
        if (!isPxtSessionKeyValid(key)) {
            throw new InvalidSessionIdException("Session id: " + sessionId + " is not valid.");
        }

        //If we made it this far, the key was ok and the sesion valid.
        return session;
    }

    /**
     * Removes all the sessions of a user. This action is useful
     * especially when we disable/deactivate a user. We donot want
     * a deactivated user's sessions to be alive..
     * @param user the user whose sessions are to be purged.
     */
    public static void purgeUserSessions(User user) {
        WebSessionFactory.purgeUserSessions(user);
    }
}