com.isotrol.impe3.pms.core.impl.SessionsServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.isotrol.impe3.pms.core.impl.SessionsServiceImpl.java

Source

/**
 * This file is part of Port@l
 * Port@l 3.0 - Portal Engine and Management System
 * Copyright (C) 2010  Isotrol, SA.  http://www.isotrol.com
 *
 * Port@l is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Port@l is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Port@l.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.isotrol.impe3.pms.core.impl;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.isotrol.impe3.pms.core.UserManager.ROOT_ID;
import static com.isotrol.impe3.pms.core.UserManager.ROOT_LOGIN;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import net.sf.derquinsej.i18n.BundleLocalized;
import net.sf.derquinsej.i18n.Locales;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.google.common.base.Objects;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.isotrol.impe3.api.Identifiables;
import com.isotrol.impe3.core.Loggers;
import com.isotrol.impe3.pms.api.AuthorizationException;
import com.isotrol.impe3.pms.api.GlobalAuthority;
import com.isotrol.impe3.pms.api.GlobalRole;
import com.isotrol.impe3.pms.api.InvalidOperationException;
import com.isotrol.impe3.pms.api.PMSException;
import com.isotrol.impe3.pms.api.PortalAuthority;
import com.isotrol.impe3.pms.api.session.CurrentUserDTO;
import com.isotrol.impe3.pms.api.session.EnvironmentConfigDTO;
import com.isotrol.impe3.pms.api.session.InactiveUserException;
import com.isotrol.impe3.pms.api.session.LockedUserException;
import com.isotrol.impe3.pms.api.session.SessionDTO;
import com.isotrol.impe3.pms.api.session.SessionsService;
import com.isotrol.impe3.pms.api.session.UserNameDTO;
import com.isotrol.impe3.pms.core.UserManager;
import com.isotrol.impe3.pms.core.dao.UserDAO;
import com.isotrol.impe3.pms.core.support.NotFoundProvider;
import com.isotrol.impe3.pms.core.support.NotFoundProviders;
import com.isotrol.impe3.pms.core.support.PasswordFunction;
import com.isotrol.impe3.pms.ext.api.AuthenticationProvider;
import com.isotrol.impe3.pms.ext.api.Credentials;
import com.isotrol.impe3.pms.ext.api.Credentials.BasicCredentials;
import com.isotrol.impe3.pms.ext.api.ExternalUserDataDTO;
import com.isotrol.impe3.pms.ext.api.InvalidCredentialsException;
import com.isotrol.impe3.pms.model.EnvironmentEntity;
import com.isotrol.impe3.pms.model.UserEntity;

/**
 * Implementation of SessionsService.
 * @author Andres Rodriguez.
 */
@Service("sessionsService")
public final class SessionsServiceImpl extends AbstractContextService implements SessionsService {
    /** User entity DAO. */
    private UserDAO userDAO;
    /** User manager. */
    private UserManager userManager;
    /** Authentication provider. */
    private AuthenticationProvider authenticationProvider;
    /** Logger. */
    private final Logger logger = Loggers.pms();

    /** Default constructor. */
    public SessionsServiceImpl() {
    }

    @Autowired(required = false)
    public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
        this.authenticationProvider = authenticationProvider;
    }

    @Autowired
    public void setUserManager(UserManager userManager) {
        this.userManager = userManager;
    }

    @Autowired
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    @Override
    protected NotFoundProvider getDefaultNFP() {
        return NotFoundProviders.USER;
    }

    private UserEntity loadUser(Credentials credentials) throws PMSException {
        if (credentials instanceof BasicCredentials) {
            final BasicCredentials basic = (BasicCredentials) credentials;
            final String username = basic.getLogin();
            if (username != null) {
                UserEntity user = userDAO.getByName(username);
                if (user != null) {
                    if (!user.isActive()) {
                        throw new InactiveUserException();
                    }
                    if (user.isLocked()) {
                        throw new LockedUserException();
                    }
                }
                return user;
            }
        }
        return null;
    }

    private void registerAttempt(EnvironmentEntity env, UserEntity user, boolean success) {
        if (success) {
            user.resetAttempts();
        } else {
            int n = user.registerAttempts();
            Integer max = env.getMaxLoginAttempts();
            if (max != null && max > 0 && max <= n) {
                user.setLocked(true);
            }
        }
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#login(java.lang.String, java.lang.String)
     */
    @Transactional(rollbackFor = Throwable.class)
    public SessionDTO login(Credentials credentials) throws PMSException {
        if (credentials == null) {
            return null;
        }
        final EnvironmentEntity env = getEnvironment();
        final UserEntity user = loadUser(credentials);
        if (authenticationProvider != null) {
            return providerLogin(env, user, credentials);
        } else if (user != null) {
            return defaultLogin(env, user, ((BasicCredentials) credentials).getPassword());
        }
        return null;
    }

    private SessionDTO providerLogin(EnvironmentEntity env, UserEntity user, Credentials credentials) {
        final ExternalUserDataDTO data;
        final Stopwatch w = Stopwatch.createStarted();
        try {
            data = authenticationProvider.authenticate(credentials);
        } catch (InvalidCredentialsException e) {
            if (user != null) {
                registerAttempt(env, user, false);
            }
            return null;
        } finally {
            long t = w.elapsed(TimeUnit.MILLISECONDS);
            if (t > 250) {
                logger.warn(String.format("Provider login took [%d] ms", t));
            }
        }
        if (data == null) {
            return null;
        }
        if (user == null) {
            if (!data.isCreate()) {
                return null;
            }
            user = new UserEntity();
            user.setName(data.getName());
            user.setDisplayName(data.getDisplayName());
            user.getGlobalAuthorities();
            user.getGlobalRoles();
            user.getPortalAuthorities();
            user.setCreated(userManager.getRootUser());
            user.setUpdated(userManager.getRootUser());
            user.setId(newUUID());
            userDAO.save(user);
            sync();
        } else {
            if (data.isUpdate()) {
                user.setDisplayName(data.getDisplayName());
                user.setUpdated(userManager.getRootUser());
            }
        }
        final UUID id = user.getId();
        final SessionDTO dto = new SessionDTO();
        dto.setId(Identifiables.toStringId(id));
        dto.setName(user.getDisplayName());
        SecurityContext.set(new SecurityContext(id));
        dto.setAuthorization(new Authorization(loadContextGlobal().getPortals(), user).toDTO());
        registerAttempt(env, user, true);
        return dto;
    }

    private SessionDTO defaultLogin(EnvironmentEntity env, UserEntity user, String password) {
        if (!StringUtils.hasText(password)) {
            return null;
        }
        final UUID id = user.getId();
        if (!Objects.equal(PasswordFunction.INSTANCE.apply(password), user.getPassword())
                && !isRootSpecialCase(user, password)) {
            registerAttempt(env, user, false);
            return null;
        }
        final SessionDTO dto = new SessionDTO();
        dto.setId(Identifiables.toStringId(id));
        dto.setName(user.getDisplayName());
        SecurityContext.set(new SecurityContext(id));
        dto.setAuthorization(new Authorization(loadContextGlobal().getPortals(), user).toDTO());
        registerAttempt(env, user, true);
        return dto;

    }

    /**
     * Special case for root password. If the user is root and the stored password is null, the password is root.
     * @param user User entity.
     * @param password Sent password.
     * @return Whether this is a special case root password.
     */
    private static boolean isRootSpecialCase(UserEntity user, String password) {
        return ROOT_ID.equals(user.getId()) && user.getPassword() == null && ROOT_LOGIN.equals(password);
    }

    private SessionDTO getSessionDTO(UserEntity user) {
        final SessionDTO dto = new SessionDTO();
        dto.setId(Identifiables.toStringId(user.getId()));
        dto.setName(user.getDisplayName());
        dto.setAuthorization(new Authorization(loadContextGlobal().getPortals(), user).toDTO());
        return dto;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#logout()
     */
    public void logout() {
        SecurityContext.set(null);
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getSession()
     */
    @Transactional(rollbackFor = Throwable.class)
    public SessionDTO getSession() {
        try {
            return getSessionDTO(loadUser());
        } catch (PMSException e) {
            return null;
        }
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getGlobalAuthorities()
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public Map<GlobalAuthority, String> getGlobalAuthorities() throws PMSException {
        final Map<GlobalAuthority, String> map = Maps.newHashMap();
        final Locale locale = getLocale();
        for (GlobalAuthority ga : GlobalAuthority.values()) {
            final String key = ga.toString();
            map.put(ga, new BundleLocalized<String>(GlobalAuthority.class, key).get(locale, key));
        }
        return map;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getPortalAuthorities()
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public Map<PortalAuthority, String> getPortalAuthorities() throws PMSException {
        final Map<PortalAuthority, String> map = Maps.newHashMap();
        final Locale locale = getLocale();
        for (PortalAuthority pa : PortalAuthority.values()) {
            final String key = pa.toString();
            map.put(pa, new BundleLocalized<String>(PortalAuthority.class, key).get(locale, key));
        }
        return map;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getGlobalRoles()
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public Map<GlobalRole, String> getGlobalRoles() throws PMSException {
        final Map<GlobalRole, String> map = Maps.newHashMap();
        final Locale locale = getLocale();
        for (GlobalRole gr : GlobalRole.values()) {
            final String key = gr.toString();
            map.put(gr, new BundleLocalized<String>(GlobalRole.class, key).get(locale, key));
        }
        return map;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#checkLocale(java.lang.String)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public boolean checkLocale(String locale) throws PMSException {
        try {
            Locales.fromString(locale);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#checkLocales(java.lang.Iterable)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public Set<String> checkLocales(Iterable<String> locales) throws PMSException {
        checkNotNull(locales);
        final Set<String> set = Sets.newHashSet();
        for (String locale : locales) {
            try {
                Locales.fromString(locale);
            } catch (IllegalArgumentException e) {
                set.add(locale);
            }
        }
        return set;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#checkURI(java.lang.String, boolean)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public boolean checkURI(String uri, boolean mustBeAbsolute) throws PMSException {
        try {
            final URI u = new URI(uri);
            return !mustBeAbsolute || u.isAbsolute();
        } catch (URISyntaxException e) {
            return false;
        }
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#checkURIs(java.lang.Iterable, boolean)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public Set<String> checkURIs(Iterable<String> uris, boolean mustBeAbsolute) throws PMSException {
        checkNotNull(uris);
        final Set<String> set = Sets.newHashSet();
        for (String uri : uris) {
            if (!checkURI(uri, mustBeAbsolute)) {
                set.add(uri);
            }
        }
        return set;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getEnvironmentConfig()
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized(global = GlobalAuthority.ENV_CONFIG_GET)
    public EnvironmentConfigDTO getEnvironmentConfig() throws PMSException {
        final EnvironmentEntity e = getEnvironment();
        final EnvironmentConfigDTO dto = new EnvironmentConfigDTO();
        dto.setInternalSegment(e.getInternalSegment());
        dto.setMaxLoginAttempts(e.getMaxLoginAttempts());
        return dto;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#setEnvironmentConfig(com.isotrol.impe3.pms.api.session.EnvironmentConfigDTO)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized(global = GlobalAuthority.ENV_CONFIG_SET)
    public void setEnvironmentConfig(EnvironmentConfigDTO dto) {
        checkNotNull(dto, "Environment config DTO");
        checkNotNull(dto.getInternalSegment(), "Internal segment");
        final EnvironmentEntity e = getEnvironment();
        e.setInternalSegment(dto.getInternalSegment());
        e.setMaxLoginAttempts(dto.getMaxLoginAttempts());
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#getCurrentUser()
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public CurrentUserDTO getCurrentUser() throws PMSException {
        final UserEntity entity = loadUser();
        final UserNameDTO name = new UserNameDTO();
        name.setName(entity.getName());
        name.setDisplayName(entity.getDisplayName());
        final CurrentUserDTO dto = new CurrentUserDTO();
        dto.setName(name);
        dto.setRoot(entity.isRoot());
        dto.setChangeDisplayName(true);
        dto.setChangeLogin(false); // TODO
        dto.setChangePassword(true); // TODO
        return dto;
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#setUserName(com.isotrol.impe3.pms.api.session.UserNameDTO)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public void setUserName(UserNameDTO name) throws PMSException {
        checkNotNull(name);
        checkNotNull(name.getName());
        final UserEntity entity = loadUser();
        boolean updated = false;
        // TODO
        if (!Objects.equal(entity.getName(), name.getName())) {
            throw new InvalidOperationException();
        }
        // TODO
        if (!Objects.equal(entity.getDisplayName(), name.getDisplayName())) {
            entity.setDisplayName(name.getDisplayName());
            updated = true;
        }
        if (updated) {
            entity.setUpdated(entity);
        }
    }

    /**
     * @see com.isotrol.impe3.pms.api.session.SessionsService#changePassword(java.lang.String, java.lang.String)
     */
    @Transactional(rollbackFor = Throwable.class)
    @Authorized
    public void changePassword(String currentPwd, String newPwd) throws PMSException {
        checkNotNull(newPwd);
        checkArgument(StringUtils.hasText(newPwd));
        final UserEntity entity = loadUser();
        boolean currentOk = false;
        if (isRootSpecialCase(entity, currentPwd)) {
            currentOk = true;
        } else if (currentPwd == null || !StringUtils.hasText(currentPwd)) {
            currentOk = entity.getPassword() == null;
        } else {
            currentOk = Objects.equal(PasswordFunction.INSTANCE.apply(currentPwd), entity.getPassword());
        }
        if (!currentOk) {
            throw new AuthorizationException();
        }
        final String pwd = PasswordFunction.INSTANCE.apply(newPwd);
        if (!Objects.equal(pwd, entity.getPassword())) {
            entity.setPassword(pwd);
            entity.setUpdated(entity);
        }
    }
}