com.epam.ta.reportportal.core.user.impl.CreateUserHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.epam.ta.reportportal.core.user.impl.CreateUserHandler.java

Source

/*
 * Copyright 2016 EPAM Systems
 * 
 * 
 * This file is part of EPAM Report Portal.
 * https://github.com/reportportal/service-api
 * 
 * Report Portal 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.
 * 
 * Report Portal 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 Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.epam.ta.reportportal.core.user.impl;

import com.epam.ta.reportportal.commons.EntityUtils;
import com.epam.ta.reportportal.commons.Preconditions;
import com.epam.ta.reportportal.core.user.ICreateUserHandler;
import com.epam.ta.reportportal.database.dao.ProjectRepository;
import com.epam.ta.reportportal.database.dao.RestorePasswordBidRepository;
import com.epam.ta.reportportal.database.dao.UserCreationBidRepository;
import com.epam.ta.reportportal.database.dao.UserRepository;
import com.epam.ta.reportportal.database.entity.Project;
import com.epam.ta.reportportal.database.entity.Project.UserConfig;
import com.epam.ta.reportportal.database.entity.ProjectRole;
import com.epam.ta.reportportal.database.entity.user.*;
import com.epam.ta.reportportal.database.personal.PersonalProjectUtils;
import com.epam.ta.reportportal.events.UserCreatedEvent;
import com.epam.ta.reportportal.exception.ReportPortalException;
import com.epam.ta.reportportal.util.Predicates;
import com.epam.ta.reportportal.util.email.EmailService;
import com.epam.ta.reportportal.util.email.MailServiceFactory;
import com.epam.ta.reportportal.ws.converter.builders.RestorePasswordBidBuilder;
import com.epam.ta.reportportal.ws.converter.builders.UserBuilder;
import com.epam.ta.reportportal.ws.converter.builders.UserCreationBidBuilder;
import com.epam.ta.reportportal.ws.model.ErrorType;
import com.epam.ta.reportportal.ws.model.OperationCompletionRS;
import com.epam.ta.reportportal.ws.model.YesNoRS;
import com.epam.ta.reportportal.ws.model.user.*;
import com.google.common.base.Charsets;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.inject.Provider;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.epam.reportportal.commons.Safe.safe;
import static com.epam.ta.reportportal.commons.Predicates.*;
import static com.epam.ta.reportportal.commons.validation.BusinessRule.expect;
import static com.epam.ta.reportportal.commons.validation.BusinessRule.fail;
import static com.epam.ta.reportportal.commons.validation.Suppliers.formattedSupplier;
import static com.epam.ta.reportportal.database.entity.ProjectRole.CUSTOMER;
import static com.epam.ta.reportportal.database.entity.ProjectRole.forName;
import static com.epam.ta.reportportal.ws.model.ErrorType.*;

/**
 * Implementation of Create User handler
 *
 * @author Andrei_Ramanchuk
 */
@Service
public class CreateUserHandler implements ICreateUserHandler {

    static final HashFunction HASH_FUNCTION = Hashing.md5();

    private UserRepository userRepository;
    private ProjectRepository projectRepository;

    @Autowired
    private MailServiceFactory emailServiceFactory;

    @Autowired
    private UserCreationBidRepository userCreationBidRepository;

    @Autowired
    private RestorePasswordBidRepository restorePasswordBidRepository;

    private ApplicationEventPublisher eventPublisher;

    @Autowired
    private Provider<UserBuilder> userBuilder;

    @Autowired
    private Provider<UserCreationBidBuilder> userCreationBidBuilder;

    @Autowired
    private Provider<RestorePasswordBidBuilder> restorePasswordBidBuilder;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Autowired
    public void setProjectRepository(ProjectRepository projectRepository) {
        this.projectRepository = projectRepository;
    }

    @Autowired
    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Override
    public CreateUserRS createUserByAdmin(CreateUserRQFull request, String userName, String basicUrl) {
        String newUsername = EntityUtils.normalizeUsername(request.getLogin());

        expect(userRepository.exists(newUsername), equalTo(false)).verify(USER_ALREADY_EXISTS,
                formattedSupplier("login='{}'", newUsername));

        expect(newUsername, Predicates.SPECIAL_CHARS_ONLY.negate()).verify(ErrorType.INCORRECT_REQUEST,
                formattedSupplier("Username '{}' consists only of special characters", newUsername));

        String projectName = EntityUtils.normalizeProjectName(request.getDefaultProject());
        Project defaultProject = projectRepository.findOne(projectName);
        expect(defaultProject, notNull()).verify(PROJECT_NOT_FOUND, projectName);

        String email = EntityUtils.normalizeEmail(request.getEmail());
        expect(UserUtils.isEmailValid(email), equalTo(true)).verify(BAD_REQUEST_ERROR, email);

        CreateUserRQConfirm req = new CreateUserRQConfirm();
        req.setDefaultProject(projectName);
        req.setEmail(email);
        req.setFullName(request.getFullName());
        req.setLogin(request.getLogin());
        req.setPassword(request.getPassword());

        final Optional<UserRole> userRole = UserRole.findByName(request.getAccountRole());
        expect(userRole.isPresent(), equalTo(true)).verify(BAD_REQUEST_ERROR,
                "Incorrect specified Account Role parameter.");
        User user = userBuilder.get().addCreateUserRQ(req).addUserRole(userRole.get()).build();
        Optional<ProjectRole> projectRole = forName(request.getProjectRole());
        expect(projectRole, Preconditions.IS_PRESENT).verify(ROLE_NOT_FOUND, request.getProjectRole());

        Map<String, UserConfig> projectUsers = defaultProject.getUsers();
        projectUsers.put(user.getId(),
                UserConfig.newOne().withProjectRole(projectRole.get()).withProposedRole(projectRole.get()));
        defaultProject.setUsers(projectUsers);

        CreateUserRS response = new CreateUserRS();

        try {
            userRepository.save(user);
            projectRepository.addUsers(projectName, projectUsers);

            /*
             * Generate personal project for the user
             */
            Project personalProject = PersonalProjectUtils.generatePersonalProject(user);
            if (!defaultProject.getId().equals(personalProject.getId())) {
                projectRepository.save(personalProject);
            }

            safe(() -> emailServiceFactory.getDefaultEmailService(true).sendConfirmationEmail(request, basicUrl),
                    e -> response.setWarning(e.getMessage()));
        } catch (DuplicateKeyException e) {
            fail().withError(USER_ALREADY_EXISTS, formattedSupplier("email='{}'", request.getEmail()));
        } catch (Exception exp) {
            throw new ReportPortalException("Error while User creating: " + exp.getMessage(), exp);
        }

        eventPublisher.publishEvent(new UserCreatedEvent(user, userName));

        response.setLogin(user.getLogin());
        return response;
    }

    @Override
    public CreateUserBidRS createUserBid(CreateUserRQ request, Principal principal, String emailURL) {
        EmailService emailService = emailServiceFactory.getDefaultEmailService(true);

        User creator = userRepository.findOne(principal.getName());
        expect(creator, notNull()).verify(ACCESS_DENIED);

        String email = EntityUtils.normalizeEmail(request.getEmail());
        expect(UserUtils.isEmailValid(email), equalTo(true)).verify(BAD_REQUEST_ERROR, email);

        User email_user = userRepository.findByEmail(request.getEmail());
        expect(email_user, isNull()).verify(USER_ALREADY_EXISTS, formattedSupplier("email={}", request.getEmail()));

        Project defaultProject = projectRepository
                .findOne(EntityUtils.normalizeProjectName(request.getDefaultProject()));

        expect(defaultProject, notNull()).verify(PROJECT_NOT_FOUND, request.getDefaultProject());
        UserConfig userConfig = defaultProject.getUsers().get(principal.getName());
        List<Project> projects = projectRepository.findUserProjects(principal.getName());
        expect(defaultProject, not(in(projects))).verify(ACCESS_DENIED);

        Optional<ProjectRole> role = forName(request.getRole());
        expect(role, Preconditions.IS_PRESENT).verify(ROLE_NOT_FOUND, request.getRole());

        // FIXME move to controller level
        if (creator.getRole() != UserRole.ADMINISTRATOR) {
            int creatorProjectRoleLevel = userConfig.getProjectRole().getRoleLevel();
            int newUserProjectRoleLevel = role.get().getRoleLevel();
            expect(creatorProjectRoleLevel >= newUserProjectRoleLevel, equalTo(Boolean.TRUE)).verify(ACCESS_DENIED);
        }

        UserCreationBid bid = userCreationBidBuilder.get().addUserCreationBid(request).build();
        try {
            userCreationBidRepository.save(bid);
        } catch (Exception e) {
            throw new ReportPortalException("Error while user creation bid registering.", e);
        }

        StringBuilder emailLink = new StringBuilder(emailURL);
        try {
            emailLink.append("/ui/#registration?uuid=");
            emailLink.append(bid.getId());
            emailService.sendConfirmationEmail("User registration confirmation", new String[] { bid.getEmail() },
                    emailLink.toString());
        } catch (Exception e) {
            fail().withError(EMAIL_CONFIGURATION_IS_INCORRECT,
                    formattedSupplier("Unable to send email for bid '{}'." + e.getMessage(), bid.getId()));
        }

        CreateUserBidRS response = new CreateUserBidRS();
        String msg = "Bid for user creation with email '" + email
                + "' is successfully registered. Confirmation info will be send on provided email. Expiration: 1 day.";

        response.setMessage(msg);
        response.setBid(bid.getId());
        response.setBackLink(emailLink.toString());
        return response;
    }

    @Override
    public CreateUserRS createUser(CreateUserRQConfirm request, String uuid, Principal principal) {
        UserCreationBid bid = userCreationBidRepository.findOne(uuid);
        expect(bid, notNull()).verify(INCORRECT_REQUEST,
                "Impossible to register user. UUID expired or already registered.");

        User user = userRepository.findOne(request.getLogin());
        expect(user, isNull()).verify(USER_ALREADY_EXISTS, formattedSupplier("login='{}'", request.getLogin()));

        expect(request.getLogin(), Predicates.SPECIAL_CHARS_ONLY.negate()).verify(ErrorType.INCORRECT_REQUEST,
                formattedSupplier("Username '{}' consists only of special characters", request.getLogin()));

        // synchronized (this)
        Project defaultProject = projectRepository.findOne(bid.getDefaultProject());
        expect(defaultProject, notNull()).verify(PROJECT_NOT_FOUND, bid.getDefaultProject());

        // populate field from existing bid record
        request.setDefaultProject(bid.getDefaultProject());

        String email = request.getEmail();
        expect(UserUtils.isEmailValid(email), equalTo(true)).verify(BAD_REQUEST_ERROR, email);

        User email_user = userRepository.findByEmail(request.getEmail());
        expect(email_user, isNull()).verify(USER_ALREADY_EXISTS,
                formattedSupplier("email='{}'", request.getEmail()));

        user = userBuilder.get().addCreateUserRQ(request).addUserRole(UserRole.USER).build();
        Optional<ProjectRole> projectRole = forName(bid.getRole());
        expect(projectRole, Preconditions.IS_PRESENT).verify(ROLE_NOT_FOUND, bid.getRole());

        Map<String, UserConfig> projectUsers = defaultProject.getUsers();
        if (projectRole.get().equals(CUSTOMER)) {
            projectUsers.put(user.getId(),
                    UserConfig.newOne().withProjectRole(CUSTOMER).withProposedRole(CUSTOMER));
        } else {
            projectUsers.put(user.getId(),
                    UserConfig.newOne().withProjectRole(projectRole.get()).withProposedRole(projectRole.get()));
        }
        defaultProject.setUsers(projectUsers);

        try {
            userRepository.save(user);
            projectRepository.addUsers(request.getDefaultProject(), projectUsers);

            /*
             * Generate personal project for the user
             */
            Project personalProject = PersonalProjectUtils.generatePersonalProject(user);
            if (!defaultProject.getId().equals(personalProject.getId())) {
                projectRepository.save(personalProject);
            }

            userCreationBidRepository.delete(uuid);
        } catch (DuplicateKeyException e) {
            fail().withError(USER_ALREADY_EXISTS, formattedSupplier("email='{}'", request.getEmail()));
        } catch (Exception exp) {
            throw new ReportPortalException("Error while User creating.", exp);
        }

        eventPublisher.publishEvent(new UserCreatedEvent(user, user.getLogin()));
        CreateUserRS response = new CreateUserRS();
        response.setLogin(user.getLogin());
        return response;
    }

    @Override
    public OperationCompletionRS createRestorePasswordBid(RestorePasswordRQ rq, String baseUrl) {
        EmailService emailService = emailServiceFactory.getDefaultEmailService(true);
        String email = EntityUtils.normalizeEmail(rq.getEmail());
        expect(UserUtils.isEmailValid(email), equalTo(true)).verify(BAD_REQUEST_ERROR, email);
        User user = userRepository.findByEmail(email);
        expect(user, notNull()).verify(USER_NOT_FOUND, email);
        expect(user.getType(), equalTo(UserType.INTERNAL)).verify(BAD_REQUEST_ERROR,
                "Unable to change password for external user");
        RestorePasswordBid bid = restorePasswordBidBuilder.get().addRestorePasswordBid(rq).build();
        restorePasswordBidRepository.save(bid);
        try {
            // TODO use default 'from' param or project specified?
            emailService.sendRestorePasswordEmail("Password recovery", new String[] { rq.getEmail() },
                    baseUrl + "#login?reset=" + bid.getId(), user.getLogin());
        } catch (Exception e) {
            fail().withError(FORBIDDEN_OPERATION,
                    formattedSupplier("Unable to send email for bid '{}'.", bid.getId()));
        }
        return new OperationCompletionRS("Email has been sent");
    }

    @Override
    public OperationCompletionRS resetPassword(ResetPasswordRQ rq) {
        RestorePasswordBid bid = restorePasswordBidRepository.findOne(rq.getUuid());
        expect(bid, notNull()).verify(ACCESS_DENIED, "The password change link is no longer valid.");
        String email = bid.getEmail();
        expect(UserUtils.isEmailValid(email), equalTo(true)).verify(BAD_REQUEST_ERROR, email);
        User byEmail = userRepository.findByEmail(email);
        expect(byEmail, notNull()).verify(USER_NOT_FOUND);
        expect(byEmail.getType(), equalTo(UserType.INTERNAL)).verify(BAD_REQUEST_ERROR,
                "Unable to change password for external user");
        byEmail.setPassword(HASH_FUNCTION.hashString(rq.getPassword(), Charsets.UTF_8).toString());
        userRepository.save(byEmail);
        restorePasswordBidRepository.delete(rq.getUuid());
        OperationCompletionRS rs = new OperationCompletionRS();
        rs.setResultMessage("Password has been changed");
        return rs;
    }

    @Override
    public YesNoRS isResetPasswordBidExist(String id) {
        RestorePasswordBid bid = restorePasswordBidRepository.findOne(id);
        return new YesNoRS(null != bid);
    }

}