All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.craftercms.security.authentication.impl.RememberMeManagerImpl Maven / Gradle / Ivy

There is a newer version: 4.3.1
Show newest version
package org.craftercms.security.authentication.impl;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.craftercms.commons.crypto.CryptoException;
import org.craftercms.commons.crypto.TextEncryptor;
import org.craftercms.commons.http.CookieManager;
import org.craftercms.commons.http.HttpUtils;
import org.craftercms.commons.http.RequestContext;
import org.craftercms.profile.api.PersistentLogin;
import org.craftercms.profile.api.Profile;
import org.craftercms.profile.api.exceptions.ProfileException;
import org.craftercms.profile.api.services.AuthenticationService;
import org.craftercms.profile.api.services.ProfileService;
import org.craftercms.security.authentication.Authentication;
import org.craftercms.security.authentication.AuthenticationManager;
import org.craftercms.security.authentication.RememberMeManager;
import org.craftercms.security.exception.AuthenticationException;
import org.craftercms.security.exception.AuthenticationSystemException;
import org.craftercms.security.exception.rememberme.CookieTheftException;
import org.craftercms.security.exception.rememberme.InvalidCookieException;
import org.craftercms.security.exception.rememberme.RememberMeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

/**
 * Default implementation of {@link org.craftercms.security.authentication.RememberMeManager}.
 *
 * @author avasquez
 */
public class RememberMeManagerImpl implements RememberMeManager {

    private static final Logger logger = LoggerFactory.getLogger(AuthenticationManagerImpl.class);

    public static final String REMEMBER_ME_COOKIE_NAME = "remember-me";
    public static final char SERIALIZED_LOGIN_SEPARATOR = ':';

    protected AuthenticationService authenticationService;
    protected AuthenticationManager authenticationManager;
    protected ProfileService profileService;
    protected TextEncryptor encryptor;
    protected CookieManager rememberMeCookieManager;

    @Required
    public void setAuthenticationService(final AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }

    @Required
    public void setAuthenticationManager(final AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Required
    public void setProfileService(final ProfileService profileService) {
        this.profileService = profileService;
    }

    @Required
    public void setEncryptor(final TextEncryptor encryptor) {
        this.encryptor = encryptor;
    }

    @Required
    public void setRememberMeCookieManager(final CookieManager rememberMeCookieManager) {
        this.rememberMeCookieManager = rememberMeCookieManager;
    }

    @Override
    public Authentication autoLogin(RequestContext context) throws RememberMeException {
        PersistentLogin login = getPersistentLoginFromCookie(context.getRequest());

        if (login != null) {
            PersistentLogin actualLogin;

            try {
                actualLogin = authenticationService.getPersistentLogin(login.getId());
            } catch (ProfileException e) {
                throw new RememberMeException("Error retrieving persistent login '" + login.getProfileId() + "'");
            }

            if (actualLogin != null) {
                if (!login.getProfileId().equals(actualLogin.getProfileId())) {
                    throw new InvalidCookieException("Profile ID mismatch");
                } else if (!login.getToken().equals(actualLogin.getToken())) {
                    throw new CookieTheftException("Token mismatch. Implies a cookie theft");
                } else {
                    String loginId = actualLogin.getId();
                    String profileId = actualLogin.getProfileId();

                    logger.debug("Remember me cookie match for {}. Starting auto-login", actualLogin);

                    Authentication auth;
                    try {
                        auth = authenticate(profileId);
                    } catch (AuthenticationException e) {
                        // Delete remember me cookie so that we don't retry auto login in next request
                        disableRememberMe(loginId, context);

                        throw new RememberMeException("Unable to auto-login user '" + profileId + "'", e);
                    }

                    updateRememberMe(loginId, context);

                    return auth;
                }
            } else {
                logger.debug("No persistent login found for ID '{}' (has possibly expired)", login.getId());

                deleteRememberMeCookie(context.getResponse());

                return null;
            }
        } else {
            return null;
        }
    }

    @Override
    public void enableRememberMe(Authentication authentication, RequestContext context) throws RememberMeException {
        String profileId = authentication.getProfile().getId().toString();
        PersistentLogin login;

        try {
            login = authenticationService.createPersistentLogin(profileId);
        } catch (ProfileException e) {
            throw new RememberMeException("Error creating persistent login for profile '" + profileId + "'", e);
        }

        logger.debug("Persistent login created: {}", login);

        addRememberMeCookie(serializeLogin(login), context.getResponse());
    }

    @Override
    public void disableRememberMe(RequestContext context) throws RememberMeException {
        PersistentLogin login = getPersistentLoginFromCookie(context.getRequest());
        if (login != null) {
            disableRememberMe(login.getId(), context);
        }
    }

    protected void disableRememberMe(String loginId, RequestContext context) throws RememberMeException {
        deleteRememberMeCookie(context.getResponse());

        try {
            authenticationService.deletePersistentLogin(loginId);
        } catch (ProfileException e) {
            throw new RememberMeException("Error invalidating persistent login '" + loginId + "'");
        }

        logger.debug("Persistent login '{}' invalidated", loginId);
    }

    protected void updateRememberMe(String loginId, RequestContext context) throws RememberMeException {
        PersistentLogin login;
        try {
            login = authenticationService.refreshPersistentLoginToken(loginId);
        } catch (ProfileException e) {
            throw new RememberMeException("Unable to update persistent login '" + loginId + "'", e);
        }

        logger.debug("Persistent login updated: {}", login);

        addRememberMeCookie(serializeLogin(login), context.getResponse());
    }

    protected String serializeLogin(PersistentLogin login) throws RememberMeException {
        StringBuilder serializedLogin = new StringBuilder();
        serializedLogin.append(login.getId()).append(SERIALIZED_LOGIN_SEPARATOR);
        serializedLogin.append(login.getProfileId()).append(SERIALIZED_LOGIN_SEPARATOR);
        serializedLogin.append(login.getToken());

        try {
            return encryptor.encrypt(serializedLogin.toString());
        } catch (CryptoException e) {
            throw new RememberMeException("Unable to encrypt remember me cookie", e);
        }
    }

    protected PersistentLogin deserializeLogin(String serializedLogin) throws RememberMeException {
        String decryptedLogin;
        try {
            decryptedLogin = encryptor.decrypt(serializedLogin);
        } catch (CryptoException e) {
            throw new RememberMeException("Unable to decrypt remember me cookie", e);
        }

        String[] splitSerializedLogin = StringUtils.split(decryptedLogin, SERIALIZED_LOGIN_SEPARATOR);

        if (ArrayUtils.isNotEmpty(splitSerializedLogin) && splitSerializedLogin.length == 3) {
            PersistentLogin login = new PersistentLogin();
            login.setId(splitSerializedLogin[0]);
            login.setProfileId(splitSerializedLogin[1]);
            login.setToken(splitSerializedLogin[2]);

            return login;
        } else {
            throw new InvalidCookieException("Invalid format of remember me cookie");
        }
    }

    protected void addRememberMeCookie(String cookieValue, HttpServletResponse response) {
        rememberMeCookieManager.addCookie(REMEMBER_ME_COOKIE_NAME, cookieValue, response);
    }

    protected String getRememberMeCookie(HttpServletRequest request) {
        return HttpUtils.getCookieValue(REMEMBER_ME_COOKIE_NAME, request);
    }

    protected void deleteRememberMeCookie(HttpServletResponse response) {
        rememberMeCookieManager.deleteCookie(REMEMBER_ME_COOKIE_NAME, response);
    }

    protected PersistentLogin getPersistentLoginFromCookie(HttpServletRequest request) {
        String cookie = getRememberMeCookie(request);
        if (StringUtils.isNotEmpty(cookie)) {
            return deserializeLogin(cookie);
        } else {
            return null;
        }
    }

    protected Authentication authenticate(String profileId) throws AuthenticationException {
        Profile profile;
        try {
            profile = profileService.getProfile(profileId);
        } catch (ProfileException e) {
            throw new AuthenticationSystemException("Error retrieving profile '" + profileId + "'", e);
        }

        if (profile != null) {
            return authenticationManager.authenticateUser(profile, true);
        } else {
            throw new AuthenticationSystemException("No profile found for ID '" + profileId + "'");
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy