com.abiquo.api.services.UserService.java Source code

Java tutorial

Introduction

Here is the source code for com.abiquo.api.services.UserService.java

Source

/**
 * Abiquo community edition
 * cloud management application for hybrid clouds
 * Copyright (C) 2008-2010 - Abiquo Holdings S.L.
 *
 * This application is free software; you can redistribute it and/or
 * modify it under the terms of the GNU LESSER GENERAL PUBLIC
 * LICENSE as published by the Free Software Foundation under
 * version 3 of the License
 *
 * This software 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
 * LESSER GENERAL PUBLIC LICENSE v.3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

package com.abiquo.api.services;

import static com.abiquo.api.util.URIResolver.buildPath;

import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.abiquo.api.config.ConfigService;
import com.abiquo.api.exceptions.APIError;
import com.abiquo.api.exceptions.ConflictException;
import com.abiquo.api.resources.EnterpriseResource;
import com.abiquo.api.resources.EnterprisesResource;
import com.abiquo.api.resources.RoleResource;
import com.abiquo.api.resources.RolesResource;
import com.abiquo.api.spring.security.AbiquoUserDetails;
import com.abiquo.api.spring.security.SecurityService;
import com.abiquo.api.util.URIResolver;
import com.abiquo.model.enumerator.Privileges;
import com.abiquo.model.rest.RESTLink;
import com.abiquo.server.core.enterprise.Enterprise;
import com.abiquo.server.core.enterprise.EnterpriseRep;
import com.abiquo.server.core.enterprise.Privilege;
import com.abiquo.server.core.enterprise.Role;
import com.abiquo.server.core.enterprise.User;
import com.abiquo.server.core.enterprise.UserDto;
import com.abiquo.server.core.enterprise.User.AuthType;
import com.abiquo.tracer.ComponentType;
import com.abiquo.tracer.EventType;
import com.abiquo.tracer.SeverityType;

@Service
@Transactional(readOnly = true)
public class UserService extends DefaultApiService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);

    @Autowired
    EnterpriseRep repo;

    @Autowired
    SecurityService securityService;

    public UserService() {

    }

    // use this to initialize it for tests
    public UserService(final EntityManager em) {
        repo = new EnterpriseRep(em);
        securityService = new SecurityService();
    }

    /**
     * Based on the spring authentication context.
     * 
     * @see SecurityContextHolder
     */
    public User getCurrentUser() {
        if (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof AbiquoUserDetails) {
            AbiquoUserDetails details = (AbiquoUserDetails) SecurityContextHolder.getContext().getAuthentication()
                    .getPrincipal();

            AuthType authType = AuthType
                    .valueOf(details.getAuthType() != null ? details.getAuthType() : AuthType.ABIQUO.name());
            return repo.getUserByAuth(details.getUsername(), authType);
        } else { // Backward compatibility and bzngine

            String userName = SecurityContextHolder.getContext().getAuthentication().getName();

            return repo.getUserByAuth(userName, AuthType.ABIQUO);
        }
    }

    public Collection<User> getUsersByEnterprise(final String enterpriseId, final String filter, final String order,
            final boolean desc) {
        return getUsersByEnterprise(enterpriseId, filter, order, desc, false, 0, 25);
    }

    public Collection<User> getUsersByEnterprise(final String enterpriseId, final String filter, String order,
            final boolean desc, final boolean connected, final Integer page, final Integer numResults) {
        Enterprise enterprise = null;
        User user = getCurrentUser();

        if (!enterpriseId.equals("_")) {
            enterprise = findEnterprise(Integer.valueOf(enterpriseId));

            // [ABICLOUDPREMIUM-1310] Cloud admin can view all. Enterprise admin and users can only
            // view their enterprise: check that the provided id corresponds to their enterprise,
            // and fail if the id is invalid
            checkCurrentEnterpriseForUsers(enterprise);
        } else {
            // [ABICLOUDPREMIUM-1310] Cloud admin can view all. Enterprise admin and users can only
            // view their enterprise, so force it if necessary. Here we won't fail, because no id
            // was provided in the request
            // if (user.getRole().getType() != Role.Type.SYS_ADMIN)
            if (!securityService.isCloudAdmin()) {
                enterprise = user.getEnterprise();
            }
        }

        // [ABICLOUDPREMIUM-1310] If all the checks are valid, we still need to restrict to the
        // current user if the role of the requestes is a standard user
        // if (user.getRole().getType() == Role.Type.USER)

        // [ROLES & PRIVILEGES] User response depends on current user's privileges)

        if (!securityService.hasPrivilege(Privileges.USERS_VIEW)) {
            return Collections.singletonList(user);
        }

        if (StringUtils.isEmpty(order)) {
            order = User.NAME_PROPERTY;
        }

        Collection<User> users = repo.findUsersByEnterprise(enterprise, filter, order, desc, connected, page,
                numResults);

        // Refresh all entities to avioid lazys
        for (User u : users) {
            Hibernate.initialize(u.getEnterprise());
            Hibernate.initialize(u.getRole());

            for (Privilege p : u.getRole().getPrivileges()) {
                Hibernate.initialize(p);
            }
        }

        return users;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public User addUser(final UserDto dto, final Integer enterpriseId) {
        Role role = findRole(dto);
        return addUser(dto, enterpriseId, role);
    }

    public User addUser(final UserDto dto, final Integer enterpriseId, final Role role) {
        Enterprise enterprise = findEnterprise(enterpriseId);

        checkEnterpriseAdminCredentials(enterprise);

        if (dto.getPassword() == null || dto.getPassword().isEmpty()) {
            addValidationErrors(APIError.USER_PASSWORD_IS_NECESSARY);
            flushErrors();
        }
        if (dto.getNick() == null || dto.getNick().isEmpty()) {
            addValidationErrors(APIError.USER_NICK_IS_NECESSARY);
            flushErrors();
        }
        if (dto.getName() == null || dto.getName().isEmpty()) {
            addValidationErrors(APIError.USER_NAME_IS_NECESSARY);
            flushErrors();
        }

        User user = enterprise.createUser(role, dto.getName(), dto.getSurname(), dto.getEmail(), dto.getNick(),
                encrypt(dto.getPassword()), dto.getLocale());
        user.setActive(dto.isActive() ? 1 : 0);
        user.setDescription(dto.getDescription());
        validate(user);
        if (!securityService.hasPrivilege(Privileges.USERS_PROHIBIT_VDC_RESTRICTION, user)
                && !StringUtils.isBlank(dto.getAvailableVirtualDatacenters())) {
            user.setAvailableVirtualDatacenters(dto.getAvailableVirtualDatacenters());
        }

        if (!user.isValid()) {
            addValidationErrors(user.getValidationErrors());
            flushErrors();
        }
        if (repo.existAnyUserWithNickAndAuth(user.getNick(), AuthType.ABIQUO)) {
            addConflictErrors(APIError.USER_DUPLICATED_NICK);
            flushErrors();
        }
        if (!emailIsValid(user.getEmail())) {
            addValidationErrors(APIError.EMAIL_IS_INVALID);
            flushErrors();
        }

        repo.insertUser(user);

        tracer.log(SeverityType.INFO, ComponentType.USER, EventType.USER_CREATE, "user.created", user.getName(),
                enterprise.getName(), user.getName(), user.getSurname(), user.getRole());

        return user;
    }

    public User getUser(final Integer id) {

        return getUser(id, false);
    }

    public User getUser(final Integer id, final Boolean skipCredentials) {
        User user = repo.findUserById(id);

        if (user == null) {
            addNotFoundErrors(APIError.USER_NON_EXISTENT);
            flushErrors();
        }

        if (!skipCredentials) {
            checkUserCredentialsForSelfUser(user, user.getEnterprise());
        }

        return user;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public User modifyUser(final Integer userId, final UserDto user) {
        if (!securityService.hasPrivilege(Privileges.USERS_MANAGE_USERS)
                && !securityService.hasPrivilege(Privileges.USERS_MANAGE_OTHER_ENTERPRISES)) {
            if (!getCurrentUser().getId().equals(userId)) {
                securityService.requirePrivilege(Privileges.USERS_MANAGE_USERS);
            }
        }

        User old = repo.findUserById(userId);
        if (old == null) {
            addNotFoundErrors(APIError.USER_NON_EXISTENT);
            flushErrors();
        }

        checkUserCredentialsForSelfUser(old, old.getEnterprise());

        if (securityService.canManageOtherEnterprises(old) && !securityService.canManageOtherEnterprises()) {
            addConflictErrors(APIError.NOT_ENOUGH_PRIVILEGES);
            flushErrors();
        }

        old.setActive(user.isActive() ? 1 : 0);
        old.setEmail(user.getEmail());
        old.setLocale(user.getLocale());
        old.setName(user.getName());
        if (!StringUtils.isEmpty(user.getPassword())) {
            // Password must only be updated if it is provided
            old.setPassword(encrypt(user.getPassword()));
        }
        old.setSurname(user.getSurname());
        if (!old.getNick().equalsIgnoreCase(user.getNick())) {
            addConflictErrors(APIError.USER_NICK_CANNOT_BE_CHANGED);
            flushErrors();
        }
        old.setDescription(user.getDescription());

        if (!securityService.hasPrivilege(Privileges.USERS_PROHIBIT_VDC_RESTRICTION, old)) {
            if (StringUtils.isBlank(user.getAvailableVirtualDatacenters())) {
                user.setAvailableVirtualDatacenters(null);
            }

            old.setAvailableVirtualDatacenters(user.getAvailableVirtualDatacenters());
        }

        if (!emailIsValid(user.getEmail())) {
            addValidationErrors(APIError.EMAIL_IS_INVALID);
            flushErrors();
        }

        String authMode = ConfigService.getSecurityMode();
        if (user.searchLink(RoleResource.ROLE) != null) {

            Role newRole = findRole(user);
            if (authMode.equalsIgnoreCase(User.AuthType.LDAP.toString())) {
                if (!old.getRole().getId().equals(newRole.getId())) {

                    // In ldap mode it is not possible to edit user's role
                    throw new ConflictException(APIError.NOT_EDIT_USER_ROLE_LDAP_MODE);
                }
            }
            old.setRole(newRole);
        }

        Enterprise newEnt = null;
        if (user.searchLink(EnterpriseResource.ENTERPRISE) != null) {
            newEnt = findEnterprise(getEnterpriseID(user));
        }

        if (securityService.hasPrivilege(Privileges.USERS_MANAGE_OTHER_ENTERPRISES)) {
            if (user.searchLink(EnterpriseResource.ENTERPRISE) != null) {
                old.setEnterprise(newEnt);
            }
        } else if (securityService.hasPrivilege(Privileges.ENTERPRISE_ADMINISTER_ALL)) {
            if (getCurrentUser().getId().equals(user.getId())) {
                if (user.searchLink(EnterpriseResource.ENTERPRISE) != null) {
                    old.setEnterprise(newEnt);
                }
            }
        }

        if (!old.isValid()) {
            addValidationErrors(old.getValidationErrors());
            flushErrors();
        }
        if (repo.existAnyOtherUserWithNick(old, old.getNick())) {
            addConflictErrors(APIError.USER_DUPLICATED_NICK);
            flushErrors();
        }

        updateUser(old);

        tracer.log(SeverityType.INFO, ComponentType.USER, EventType.USER_MODIFY, "user.modified", old.getName(),
                old.getEnterprise().getName(), old.getName(), old.getSurname(), old.getRole());

        return old;
    }

    public User updateUser(final User user) {
        repo.updateUser(user);

        return user;
    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRED)
    public void removeUser(final Integer id) {
        User user = getUser(id);

        // user can not delete himself
        User logged = getCurrentUser();
        if (logged.getId() == user.getId()) {
            addConflictErrors(APIError.USER_DELETING_HIMSELF);
            flushErrors();
        }

        checkEnterpriseAdminCredentials(user.getEnterprise());

        // Cloud Admins should only be editable by other Cloud Admins
        // if (user.getRole().getType() == Role.Type.SYS_ADMIN
        // && getCurrentUser().getRole().getType() != Role.Type.SYS_ADMIN)
        if (securityService.canManageOtherEnterprises(user) && !securityService.canManageOtherEnterprises()) {
            addForbiddenErrors(APIError.NOT_ENOUGH_PRIVILEGES);
            flushErrors();
        }

        repo.removeUser(user);

        tracer.log(SeverityType.INFO, ComponentType.USER, EventType.USER_DELETE, "user.deleted", user.getName(),
                user.getEnterprise().getName(), user.getName(), user.getSurname(), user.getRole());
    }

    public boolean isAssignedTo(final Integer enterpriseId, final Integer userId) {
        User user = getUser(userId);

        return user != null && user.getEnterprise().getId().equals(enterpriseId);
    }

    private Enterprise findEnterprise(final Integer enterpriseId) {
        Enterprise enterprise = repo.findById(enterpriseId);
        if (enterprise == null) {
            addNotFoundErrors(APIError.NON_EXISTENT_ENTERPRISE);
            flushErrors();
        }
        return enterprise;
    }

    public User findUserByEnterprise(final Integer userId, final Enterprise enterprise) {
        User user = repo.findUserByEnterprise(userId, enterprise);
        if (user == null) {
            addNotFoundErrors(APIError.USER_NON_EXISTENT);
            flushErrors();
        }
        return user;
    }

    private Role findRole(final UserDto dto) {
        Role role = repo.findRoleById(getRoleId(dto));
        if (role == null) {
            addNotFoundErrors(APIError.NON_EXISTENT_ROLE);
            flushErrors();
        }
        return role;
    }

    private Integer getRoleId(final UserDto user) {
        RESTLink role = user.searchLink(RoleResource.ROLE);

        if (role == null) {
            addValidationErrors(APIError.MISSING_ROLE_LINK);
            flushErrors();
        }

        String buildPath = buildPath(RolesResource.ROLES_PATH, RoleResource.ROLE_PARAM);
        MultivaluedMap<String, String> roleValues = URIResolver.resolveFromURI(buildPath, role.getHref());

        if (roleValues == null || !roleValues.containsKey(RoleResource.ROLE)) {
            addNotFoundErrors(APIError.ROLE_PARAM_NOT_FOUND);
            flushErrors();
        }

        Integer roleId = Integer.valueOf(roleValues.getFirst(RoleResource.ROLE));
        return roleId;
    }

    private Integer getEnterpriseID(final UserDto user) {
        RESTLink ent = user.searchLink(EnterpriseResource.ENTERPRISE);

        String buildPath = buildPath(EnterprisesResource.ENTERPRISES_PATH, EnterpriseResource.ENTERPRISE_PARAM);
        MultivaluedMap<String, String> values = URIResolver.resolveFromURI(buildPath, ent.getHref());

        Integer entId = Integer.valueOf(values.getFirst(EnterpriseResource.ENTERPRISE));
        return entId;
    }

    public void checkEnterpriseAdminCredentials(final Enterprise enterprise) {
        User user = getCurrentUser();
        boolean sameEnterprise = enterprise.getId().equals(user.getEnterprise().getId());
        // Role.Type role = user.getRole().getType();
        //
        // if ((role == Role.Type.ENTERPRISE_ADMIN && !enterprise.equals(user.getEnterprise()))
        // || role == Role.Type.USER)

        if (securityService.isEnterpriseAdmin() && !sameEnterprise || securityService.isStandardUser()) {
            throw new AccessDeniedException("");
        }
    }

    public String enterpriseWithBlockedRoles(final Enterprise enterprise) {
        Collection<User> users = repo.findUsersByEnterprise(enterprise);
        for (User user : users) {
            if (user.getRole().isBlocked()) {
                return user.getRole().getName().toString();
            }
        }
        return "";
    }

    private void checkUserCredentialsForSelfUser(final User selfUser, final Enterprise enterprise) {
        User user = getCurrentUser();
        boolean sameEnterprise = enterprise.getId().equals(user.getEnterprise().getId());
        boolean sameUser = user.getId().equals(selfUser.getId());

        // Role.Type role = user.getRole().getType();
        //
        // if ((role == Role.Type.ENTERPRISE_ADMIN && !enterprise.equals(user.getEnterprise()))
        // || (role == Role.Type.USER && user.getId() != selfUser.getId()))

        if (securityService.isEnterpriseAdmin() && !sameEnterprise
                || securityService.isStandardUser() && !sameUser) {
            throw new AccessDeniedException("");
        }
    }

    public void checkCurrentEnterprise(final Enterprise enterprise) {
        User user = getCurrentUser();
        boolean sameEnterprise = enterprise.getId().equals(user.getEnterprise().getId());

        // Role.Type role = user.getRole().getType();
        // if ((role == Role.Type.ENTERPRISE_ADMIN || role == Role.Type.USER) && !sameEnterprise)
        if (!sameEnterprise && !securityService.hasPrivilege(Privileges.USERS_MANAGE_OTHER_ENTERPRISES)
                && !securityService.hasPrivilege(Privileges.USERS_MANAGE_ROLES_OTHER_ENTERPRISES)
                && !securityService.hasPrivilege(Privileges.ENTERPRISE_ENUMERATE)
                && !securityService.hasPrivilege(Privileges.ENTERPRISE_ADMINISTER_ALL)
                && !securityService.hasPrivilege(Privileges.PHYS_DC_ENUMERATE)) {
            throw new AccessDeniedException("Missing privilege to get info from other enterprises");
        }
    }

    public void checkCurrentEnterpriseForUsers(final Enterprise enterprise) {
        User user = getCurrentUser();
        boolean sameEnterprise = enterprise.getId().equals(user.getEnterprise().getId());

        if (!sameEnterprise && !securityService.hasPrivilege(Privileges.USERS_MANAGE_OTHER_ENTERPRISES)) {
            throw new AccessDeniedException("Missing privilege to get info from other enterprises");
        }
    }

    public void checkCurrentEnterpriseForPostMethods(final Enterprise enterprise) {
        User user = getCurrentUser();
        boolean sameEnterprise = enterprise.getId().equals(user.getEnterprise().getId());

        if (!sameEnterprise && !securityService.hasPrivilege(Privileges.ENTERPRISE_ADMINISTER_ALL)) {
            throw new AccessDeniedException("Missing privilege to manage info from other enterprises");
        }
    }

    /**
     * Retrieves the user by nick in the DB. This method assumes that the login is unique.
     */
    public User getUserByLogin(final String login) {
        return repo.getUserByUserName(login);
    }

    private Boolean emailIsValid(final String email) {
        if (email != null && !email.isEmpty()) {
            final Pattern pattern;
            final Matcher matchers;
            final String EMAIL_PATTERN = "[a-z0-9A-Z!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9A-Z!#$%&'*+/=?^_`{|}~-]+)*@"
                    + "(?:[a-z0-9A-Z](?:[a-z0-9A-Z-]*[a-z0-9A-Z])?\\.)+[a-z0-9A-Z](?:[a-z0-9A-Z-]*[a-z0-9A-Z])?";
            pattern = Pattern.compile(EMAIL_PATTERN);
            matchers = pattern.matcher(email);
            return matchers.matches();
        } else {
            return true;
        }
    }

    private String encrypt(final String toEncrypt) {
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            LOGGER.debug("cannot get the instance of messageDigest", e);
            // revise if the method is called from other method
            addUnexpectedErrors(APIError.STATUS_BAD_REQUEST);
            flushErrors();
        }
        messageDigest.reset();
        messageDigest.update(toEncrypt.getBytes(Charset.forName("UTF8")));
        final byte[] resultByte = messageDigest.digest();
        return new String(Hex.encodeHex(resultByte));
    }

    public SecurityService getSecurityService() {
        return securityService;
    }

    /**
     * Check if a user has permissions to use the given virtual datacenter
     * 
     * @param username nick of the given User
     * @param authtype authentication type of the given User
     * @param privileges array of strings with all privileges names from the given User role
     * @param idVdc identifier from virtual datacenter to check
     * @return True if user is allowed to user the given virtual datacenter
     */
    public boolean isUserAllowedToUseVirtualDatacenter(final String username, final String authtype,
            final String[] privileges, final Integer idVdc) {
        return repo.isUserAllowedToUseVirtualDatacenter(username, authtype, privileges, idVdc);
    }

    /**
     * Check if a user has permissions to use or see the given enterprise
     * 
     * @param username nick of the given User
     * @param authtype authentication type of the given User
     * @param privileges array of strings with all privileges names from the given User role
     * @param idEnteprise identifier from enterprise to check
     * @return True if user is allowed to use or see the given enterprise
     */
    public boolean isUserAllowedToEnterprise(final String username, final String authtype,
            final String[] privileges, final Integer idEnterprise) {
        return repo.isUserAllowedToEnterprise(username, authtype, privileges, idEnterprise);
    }
}