org.duracloud.account.db.util.impl.DuracloudUserServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.duracloud.account.db.util.impl.DuracloudUserServiceImpl.java

Source

/*
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 *     http://duracloud.org/license/
 */
package org.duracloud.account.db.util.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.duracloud.account.config.AmaEndpoint;
import org.duracloud.account.db.model.AccountInfo;
import org.duracloud.account.db.model.AccountRights;
import org.duracloud.account.db.model.DuracloudGroup;
import org.duracloud.account.db.model.DuracloudUser;
import org.duracloud.account.db.model.Role;
import org.duracloud.account.db.model.UserInvitation;
import org.duracloud.account.db.repo.DuracloudGroupRepo;
import org.duracloud.account.db.repo.DuracloudRepoMgr;
import org.duracloud.account.db.repo.DuracloudRightsRepo;
import org.duracloud.account.db.repo.DuracloudUserInvitationRepo;
import org.duracloud.account.db.repo.DuracloudUserRepo;
import org.duracloud.account.db.util.DuracloudUserService;
import org.duracloud.account.db.util.error.DBNotFoundException;
import org.duracloud.account.db.util.error.InvalidPasswordException;
import org.duracloud.account.db.util.error.InvalidRedemptionCodeException;
import org.duracloud.account.db.util.error.InvalidUsernameException;
import org.duracloud.account.db.util.error.ReservedPrefixException;
import org.duracloud.account.db.util.error.ReservedUsernameException;
import org.duracloud.account.db.util.error.UnsentEmailException;
import org.duracloud.account.db.util.error.UserAlreadyExistsException;
import org.duracloud.account.db.util.notification.NotificationMgr;
import org.duracloud.account.db.util.notification.Notifier;
import org.duracloud.common.model.Credential;
import org.duracloud.common.sns.AccountChangeNotifier;
import org.duracloud.common.util.ChecksumUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * @author Andrew Woods
 *         Date: Oct 9, 2010
 */
@Component("duracloudUserService")
public class DuracloudUserServiceImpl implements DuracloudUserService, UserDetailsService {

    private Logger log = LoggerFactory.getLogger(DuracloudUserServiceImpl.class);

    private DuracloudRepoMgr repoMgr;
    private NotificationMgr notificationMgr;
    private Notifier notifier;
    private AmaEndpoint amaEndpoint;
    private AccountChangeNotifier accountChangeNotifier;

    @Autowired
    public DuracloudUserServiceImpl(DuracloudRepoMgr duracloudRepoMgr, NotificationMgr notificationMgr,
            AmaEndpoint amaEndpoint, AccountChangeNotifier accountChangeNotifier) {
        this.repoMgr = duracloudRepoMgr;
        this.notificationMgr = notificationMgr;
        this.amaEndpoint = amaEndpoint;
        this.accountChangeNotifier = accountChangeNotifier;
    }

    @Override
    public void checkUsername(String username) throws InvalidUsernameException, UserAlreadyExistsException {

        if (!isValidUsername(username)) {
            throw new InvalidUsernameException(username);
        }

        if (isReservedPrefix(username)) {
            throw new ReservedPrefixException(username);
        }

        if (isReservedName(username)) {
            throw new ReservedUsernameException(username);
        }

        DuracloudUser user = repoMgr.getUserRepo().findByUsername(username);
        if (user != null) {
            throw new UserAlreadyExistsException(username);
        }
    }

    private boolean isValidUsername(String username) {
        if (username == null) {
            return false;
        }

        return username.matches("\\A(?![_.@\\-])[a-z0-9_.@\\-]+(?<![_.@\\-])\\Z");
    }

    private boolean isReservedName(String username) {
        return false;
    }

    private boolean isReservedPrefix(String username) {
        if (username.startsWith(DuracloudGroup.PREFIX)) {
            return true;
        }

        return false;
    }

    @Override
    public DuracloudUser createNewUser(String username, String password, String firstName, String lastName,
            String email, String securityQuestion, String securityAnswer)
            throws UserAlreadyExistsException, InvalidUsernameException {

        checkUsername(username);

        ChecksumUtil util = new ChecksumUtil(ChecksumUtil.Algorithm.SHA_256);

        DuracloudUser user = new DuracloudUser();
        user.setUsername(username);
        user.setPassword(util.generateChecksum(password));
        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        user.setSecurityQuestion(securityQuestion);
        user.setSecurityAnswer(securityAnswer);
        repoMgr.getUserRepo().save(user);

        log.info("New user created with username {}", username);
        getNotifier().sendNotificationCreateNewUser(user);

        if (user.isRoot()) {
            this.accountChangeNotifier.rootUsersChanged();
        }

        return user;
    }

    @Override
    public boolean setUserRights(Long acctId, Long userId, Role... roles) {
        return setUserRightsInternal(acctId, userId, roles);
    }

    private boolean setUserRightsInternal(Long acctId, Long userId, Role... roles) {

        Set<Role> roleSet = new HashSet<Role>();
        for (Role role : roles) {
            roleSet.add(role);
        }

        log.info("Updating user rights for user {} on account {} to roles " + asString(roleSet), userId, acctId);

        boolean result = doSetUserRights(acctId, userId, roleSet);
        if (result) {
            notifyAccountChanged(acctId);
        }
        return result;
    }

    private void notifyAccountChanged(Long acctId) {
        AccountInfo account = this.repoMgr.getAccountRepo().getOne(acctId);
        String accountId = account.getSubdomain();
        this.accountChangeNotifier.accountChanged(accountId);
    }

    private boolean doSetUserRights(Long acctId, Long userId, Set<Role> roles) {
        if (null == roles) {
            throw new IllegalArgumentException("Role may not be null");
        }

        Set<Role> oldRoles = null;
        Set<Role> newRoles = new HashSet<Role>();
        for (Role role : roles) {
            newRoles.addAll(role.getRoleHierarchy());
        }

        DuracloudRightsRepo rightsRepo = repoMgr.getRightsRepo();
        AccountRights rights = rightsRepo.findByAccountIdAndUserId(acctId, userId);
        if (rights != null) {
            oldRoles = rights.getRoles();
        } else {
            log.info("New rights will be added for user {} on account {}", userId, acctId);
        }

        boolean updatedNeeded = !newRoles.equals(oldRoles);
        if (updatedNeeded) {
            saveRights(acctId, userId, newRoles, rights);
        }
        return updatedNeeded;
    }

    private String asString(Set<Role> roles) {
        StringBuilder sb = new StringBuilder();
        for (Role role : roles) {
            sb.append(role.name());
            sb.append(",");
        }

        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }

        return sb.toString();
    }

    private void saveRights(Long acctId, Long userId, Set<Role> roles, AccountRights rights) {
        if (null == rights) {
            AccountInfo account = repoMgr.getAccountRepo().findOne(acctId);
            DuracloudUser user = repoMgr.getUserRepo().findOne(userId);

            rights = new AccountRights();
            rights.setAccount(account);
            rights.setUser(user);
            rights.setRoles(roles);
        } else {
            rights.setRoles(roles);
        }

        repoMgr.getRightsRepo().save(rights);
    }

    @Override
    public void revokeUserRights(Long acctId, Long userId) {
        log.info("Revoking rights for user {} on account {}", userId, acctId);

        doRevokeUserRights(acctId, userId);
        removeUserFromAccountGroups(acctId, userId);
        notifyAccountChanged(acctId);
    }

    private void doRevokeUserRights(Long acctId, Long userId) {
        DuracloudRightsRepo rightsRepo = repoMgr.getRightsRepo();
        AccountRights rights = rightsRepo.findByAccountIdAndUserId(acctId, userId);
        if (rights != null) {
            DuracloudUserRepo userRepo = repoMgr.getUserRepo();
            DuracloudUser user = userRepo.findOne(userId);
            user.getAccountRights().remove(rights);
            userRepo.saveAndFlush(user);
            rightsRepo.delete(rights.getId());
        }
    }

    private void removeUserFromAccountGroups(Long acctId, Long userId) {
        DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
        DuracloudGroupRepo groupRepo = repoMgr.getGroupRepo();
        List<DuracloudGroup> acctGroups = groupRepo.findByAccountId(acctId);
        for (DuracloudGroup group : acctGroups) {
            Set<DuracloudUser> groupUsers = group.getUsers();
            if (groupUsers.contains(user)) {
                groupUsers.remove(user);
                groupRepo.save(group);
            }
        }
    }

    @Override
    public void changePassword(Long userId, String oldPassword, boolean oldPasswordEncoded, String newPassword)
            throws DBNotFoundException, InvalidPasswordException {
        if (null != newPassword && !newPassword.equals(oldPassword)) {
            log.info("Changing password for user with ID {}", userId);

            ChecksumUtil util = new ChecksumUtil(ChecksumUtil.Algorithm.SHA_256);

            DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
            if (user == null) {
                throw new DBNotFoundException("User with ID: " + userId + " does not exist.");
            }
            if (!oldPasswordEncoded) {
                oldPassword = util.generateChecksum(oldPassword);
            }
            if (!user.getPassword().equals(oldPassword)) {
                throw new InvalidPasswordException(userId);
            }

            user.setPassword(util.generateChecksum(newPassword));
            repoMgr.getUserRepo().save(user);

            propagateUserUpdate(userId);
        }
    }

    @Override
    public void changePasswordInternal(Long userId, String oldPassword, boolean oldPasswordEncoded,
            String newPassword) throws DBNotFoundException, InvalidPasswordException {
        changePassword(userId, oldPassword, oldPasswordEncoded, newPassword);
    }

    @Override
    public void redeemPasswordChangeRequest(Long userId, String redemptionCode)
            throws InvalidRedemptionCodeException {

        log.info("Redeeming change request for user with ID {}", userId);

        DuracloudUserInvitationRepo invRepo = repoMgr.getUserInvitationRepo();

        // Retrieve the invitation
        UserInvitation invitation = invRepo.findByRedemptionCode(redemptionCode);
        if (invitation == null) {
            throw new InvalidRedemptionCodeException(redemptionCode);
        }

        // Delete the invitation
        invRepo.delete(invitation.getId());
    }

    private void propagateUserUpdate(Long userId) {
        DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
        // Propagate changes for each of the user's accounts
        if (!user.isRoot()) { // Do no propagate if user is root

            List<AccountRights> rightsList = repoMgr.getRightsRepo().findByUserId(userId);

            for (AccountRights rights : rightsList) {
                this.accountChangeNotifier.userStoreChanged(rights.getAccount().getSubdomain());
            }
        } else {
            this.accountChangeNotifier.rootUsersChanged();
        }
    }

    @Override
    public void forgotPassword(String username, String securityQuestion, String securityAnswer)
            throws DBNotFoundException, InvalidPasswordException, UnsentEmailException {
        log.info("Resolving forgotten password for user {}", username);

        DuracloudUser user = loadDuracloudUserByUsernameInternal(username);

        if (!user.getSecurityQuestion().equalsIgnoreCase(securityQuestion)
                || !user.getSecurityAnswer().equalsIgnoreCase(securityAnswer)) {
            throw new InvalidPasswordException(user.getId());
        }

        ChecksumUtil cksumUtil = new ChecksumUtil(ChecksumUtil.Algorithm.MD5);
        String code = username + System.currentTimeMillis();
        String redemptionCode = cksumUtil.generateChecksum(code);
        int expirationDays = 14;
        UserInvitation userInvitation = new UserInvitation(null, null, "n/a", "n/a", "n/a", "n/a", username,
                user.getEmail(), expirationDays, redemptionCode);

        this.repoMgr.getUserInvitationRepo().save(userInvitation);

        getNotifier().sendNotificationPasswordReset(user, redemptionCode, userInvitation.getExpirationDate());
    }

    @Override
    public UserInvitation retrievePassordChangeInvitation(String redemptionCode) throws DBNotFoundException {
        UserInvitation invite = this.repoMgr.getUserInvitationRepo().findByRedemptionCode(redemptionCode);
        if (invite == null) {
            throw new DBNotFoundException(
                    "Change password invitation with" + " redemption code: " + redemptionCode + " does not exist");
        }
        if (invite.getExpirationDate().getTime() < System.currentTimeMillis()) {
            log.info("invitation {} has expired. Deleting from repo...", invite);
            this.repoMgr.getUserInvitationRepo().delete(invite.getId());
            throw new DBNotFoundException("Invitation has expired: " + invite);
        } else {
            return invite;
        }
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        try {
            return loadDuracloudUserByUsername(username);
        } catch (DBNotFoundException e) {
            throw new UsernameNotFoundException(e.getMessage());
        }
    }

    @Override
    public DuracloudUser loadDuracloudUserByUsername(String username) throws DBNotFoundException {
        return loadDuracloudUserByUsernameInternal(username);
    }

    @Override
    public DuracloudUser loadDuracloudUserByUsernameInternal(String username) throws DBNotFoundException {
        DuracloudUser user = repoMgr.getUserRepo().findByUsername(username);
        if (user == null) {
            throw new DBNotFoundException("User with username: " + username + " does not exist");
        }
        return user;
    }

    @Override
    public DuracloudUser loadDuracloudUserByIdInternal(Long userId) throws DBNotFoundException {
        DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
        if (user == null) {
            throw new DBNotFoundException("User with ID: " + userId + " does not exist");
        }
        return user;
    }

    @Override
    public Long redeemAccountInvitation(Long userId, String redemptionCode) throws InvalidRedemptionCodeException {
        log.info("Redeeming account invitation for user with ID {}", userId);

        DuracloudUserInvitationRepo invRepo = repoMgr.getUserInvitationRepo();

        // Retrieve the invitation
        UserInvitation invitation = invRepo.findByRedemptionCode(redemptionCode);
        if (invitation == null) {
            throw new InvalidRedemptionCodeException(redemptionCode);
        }

        // Add the user to the account if they are not already a member
        if (!userHasAccountRights(userId, invitation.getAccount().getId())) {
            setUserRights(invitation.getAccount().getId(), userId, Role.ROLE_USER);
        }

        // Delete the invitation
        invRepo.delete(invitation.getId());

        DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
        DuracloudUser adminUser = repoMgr.getUserRepo().findByUsername(invitation.getAdminUsername());
        if (adminUser == null) {
            String msg = "Exception encountered attempting to send admin user " + invitation.getAdminUsername()
                    + " notice that user with id " + userId + " accepted their account invitation";
            DBNotFoundException e = new DBNotFoundException(
                    "Admin user with" + " username: " + invitation.getAdminUsername() + "does not exist");
            throw new UnsentEmailException(msg, e);
        }
        getNotifier().sendNotificationRedeemedInvitation(user, adminUser.getEmail());

        //return accountId
        return invitation.getAccount().getId();
    }

    private boolean userHasAccountRights(Long userId, Long accountId) {
        DuracloudRightsRepo rightsRepo = repoMgr.getRightsRepo();
        AccountRights rights = rightsRepo.findByAccountIdAndUserId(accountId, userId);
        return rights != null;
    }

    @Override
    public void storeUserDetails(Long userId, String firstName, String lastName, String email,
            String securityQuestion, String securityAnswer, String allowableIPAddressRange)
            throws DBNotFoundException {
        log.info("Updating user details for user with ID {}", userId);

        DuracloudUser user = repoMgr.getUserRepo().findOne(userId);
        if (user == null) {
            throw new DBNotFoundException("User with ID: " + userId + " does not exist");
        }
        boolean emailUpdate = !user.getEmail().equals(email);
        boolean ipAddressUpdate = !Objects.equals(user.getAllowableIPAddressRange(), allowableIPAddressRange);

        user.setFirstName(firstName);
        user.setLastName(lastName);
        user.setEmail(email);
        user.setSecurityQuestion(securityQuestion);
        user.setSecurityAnswer(securityAnswer);
        user.setAllowableIPAddressRange(allowableIPAddressRange);
        repoMgr.getUserRepo().save(user);

        if (emailUpdate || ipAddressUpdate) {
            propagateUserUpdate(userId);
        }
    }

    private Notifier getNotifier() {
        if (null == notifier) {
            notifier = new Notifier(notificationMgr.getEmailer(), amaEndpoint);
        }
        return notifier;
    }

    @Override
    public boolean addUserToAccount(Long acctId, Long userId) throws DBNotFoundException {
        boolean added = setUserRightsInternal(acctId, userId, Role.ROLE_USER);
        if (added) {
            DuracloudUser user = loadDuracloudUserByIdInternal(userId);
            AccountInfo accountInfo = repoMgr.getAccountRepo().findOne(acctId);
            if (accountInfo == null) {
                throw new DBNotFoundException("Account with ID: " + acctId + " does not exist");
            }
            getNotifier().sendNotificationUserAddedToAccount(user, accountInfo);
        }

        return added;
    }

}