org.duracloud.account.app.controller.UserController.java Source code

Java tutorial

Introduction

Here is the source code for org.duracloud.account.app.controller.UserController.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.app.controller;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.duracloud.account.config.AmaEndpoint;
import org.duracloud.account.db.model.AccountInfo;
import org.duracloud.account.db.model.AccountInfo.AccountStatus;
import org.duracloud.account.db.model.DuracloudUser;
import org.duracloud.account.db.model.InstanceType;
import org.duracloud.account.db.model.UserInvitation;
import org.duracloud.account.db.model.util.DuracloudAccount;
import org.duracloud.account.db.util.AccountManagerService;
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.UnsentEmailException;
import org.duracloud.account.util.UserFeedbackUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.Severity;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;

/**
 * The default view for this application
 * 
 * @contributor dbernstein
 */
@Controller
@Lazy
@RequestMapping(AbstractController.USERS_MAPPING)
public class UserController extends AbstractController {

    protected static final String FORGOT_PASSWORD_SUCCESS_VIEW = "forgot-password-success";

    private static final String ANONYMOUS_CHANGE_PASSWORD_VIEW = "anonymous-change-password";

    private static final String ANONYMOUS_CHANGE_PASSWORD_FAILURE_VIEW = "anonymous-change-password-failure";

    public static final String USER_KEY = "user";

    public static final String NEW_USER_VIEW = "user-new";

    public static final String FORGOT_PASSWORD_VIEW = "forgot-password";

    public static final String USER_HOME = "user-home";

    public static final String FORGOT_PASSWORD_MAPPING = "/forgot-password";

    public static final String USER_ACCOUNTS = "user-accounts";

    public static final String USER_EDIT_MAPPING = USER_MAPPING + EDIT_MAPPING;

    public static final String USER_ACCOUNTS_MAPPING = USER_MAPPING + "/accounts";

    public static final String USER_EDIT_VIEW = "user-edit";

    public static final String CHANGE_PASSWORD_MAPPING = USER_MAPPING + "/change-password";
    public static final String CHANGE_PASSWORD_VIEW = "user-change-password";
    public static final String USER_PROFILE_FORM_KEY = "userProfileEditForm";
    public static final String CHANGE_PASSWORD_FORM_KEY = "changePasswordForm";
    public static final String FORGOT_PASSWORD_FORM_KEY = "forgotPasswordForm";
    public static final String NEW_USER_FORM_KEY = "newUserForm";
    public static final String NEW_INSTANCE_FORM = "instanceForm";
    @Autowired
    private AccountManagerService accountManagerService;

    @Autowired
    private DuracloudUserService userService;

    @Autowired
    private AmaEndpoint amaEndpoint;

    /**
     * 
     * @param userService
     */
    public void setUserService(DuracloudUserService userService) {
        if (userService == null) {
            throw new NullPointerException("userService must be non-null");
        }

        this.userService = userService;
        log.info("new instance created: " + getClass().getName());
    }

    public DuracloudUserService getUserService() {
        return this.userService;
    }

    @RequestMapping(value = { NEW_MAPPING }, method = RequestMethod.GET)
    public ModelAndView getNewForm(HttpServletRequest request) {
        log.info("serving up NewUserForm");
        NewUserForm newUserForm = new NewUserForm();
        newUserForm.setRedemptionCode(removeRedemptionCodeFromSession(request));
        return new ModelAndView(NEW_USER_VIEW, NEW_USER_FORM_KEY, newUserForm);
    }

    @RequestMapping(value = { FORGOT_PASSWORD_MAPPING }, method = RequestMethod.GET)
    public ModelAndView getForgotPasswordForm(HttpServletRequest request) {
        log.info("serving up ForgotPasswordForm");
        ForgotPasswordForm forgotPasswordForm = new ForgotPasswordForm();
        return new ModelAndView(FORGOT_PASSWORD_VIEW, FORGOT_PASSWORD_FORM_KEY, forgotPasswordForm);
    }

    @ModelAttribute("instanceTypes")
    public InstanceType[] instanceTypes() {
        return InstanceType.values();
    }

    @RequestMapping(value = { "/profile" }, method = RequestMethod.GET)
    public ModelAndView profileRedirect() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth.isAuthenticated() && auth instanceof AnonymousAuthenticationToken) {
            //this check is necessary because on logout the browser is getting directed here
            //I'm not sure why the request is getting through - everything seems properly configured
            //in security-config.xml
            auth.setAuthenticated(false);
            return new ModelAndView("redirect:/users/profile");
        }
        String username = auth.getName();
        return new ModelAndView(formatUserRedirect(username));
    }

    @RequestMapping(value = { USER_MAPPING }, method = RequestMethod.GET)
    public ModelAndView getUser(@PathVariable String username, HttpServletRequest request)
            throws DBNotFoundException {
        log.debug("getting user {}", username);
        // if there's a redemption code in the session, it means that
        // the user logged in in order to redeem an invitation
        String redemptionCode = removeRedemptionCodeFromSession(request);
        if (redemptionCode != null) {
            log.debug("redemption code found in session: {}", redemptionCode);
            DuracloudUser user = this.userService.loadDuracloudUserByUsername(username);
            try {
                this.userService.redeemAccountInvitation(user.getId(), redemptionCode);
                user = this.userService.loadDuracloudUserByUsername(username);
            } catch (InvalidRedemptionCodeException e) {
                log.error("redemption failed for {} on redemption {}", username, redemptionCode);
                addRedemptionFailedMessage();
            }
        }

        return getUserAccounts(username);
    }

    /**
     * 
     */
    private void addRedemptionFailedMessage() {
        addErrorMessage("The redemption code you supplied is invalid. We are unable to add you to an account.");
    }

    /**
     * @param request
     * @return
     */
    private String removeRedemptionCodeFromSession(HttpServletRequest request) {
        String redemptionCode = (String) request.getSession().getAttribute("redemptionCode");
        if (redemptionCode != null) {
            request.getSession().removeAttribute("redemptionCode");
        }
        return redemptionCode;
    }

    @RequestMapping(value = { USER_ACCOUNTS_MAPPING }, method = RequestMethod.GET)
    public ModelAndView getUserAccounts(@PathVariable String username) throws DBNotFoundException {
        log.debug("getting user accounts for {}", username);
        ModelAndView mav = new ModelAndView(USER_ACCOUNTS);
        prepareModel(username, mav);
        return mav;
    }

    @RequestMapping(value = { USER_EDIT_MAPPING }, method = RequestMethod.GET)
    public String edit(@PathVariable String username, Model model) throws DBNotFoundException {
        log.debug("getting user accounts for {}", username);
        UserProfileEditForm form = new UserProfileEditForm();
        DuracloudUser user = this.userService.loadDuracloudUserByUsername(username);
        form.setFirstName(user.getFirstName());
        form.setLastName(user.getLastName());
        form.setEmail(user.getEmail());
        form.setAllowableIPAddressRange(user.getAllowableIPAddressRange());
        form.setSecurityQuestion(user.getSecurityQuestion());
        form.setSecurityAnswer(user.getSecurityAnswer());
        model.addAttribute(USER_PROFILE_FORM_KEY, form);

        model.addAttribute(CHANGE_PASSWORD_FORM_KEY, new ChangePasswordForm());

        addUserToModel(user, model);
        return USER_EDIT_VIEW;
    }

    @Transactional
    @RequestMapping(value = { USER_EDIT_MAPPING }, method = RequestMethod.POST)
    public ModelAndView update(@PathVariable String username,
            @ModelAttribute(USER_PROFILE_FORM_KEY) @Valid UserProfileEditForm form, BindingResult result,
            Model model) throws Exception {

        if (result.hasErrors()) {
            log.debug("profile form has errors for {}: returning...", username);
            model.addAttribute(CHANGE_PASSWORD_FORM_KEY, new ChangePasswordForm());

            DuracloudUser user = this.userService.loadDuracloudUserByUsername(username);
            addUserToModel(user, model);

            return new ModelAndView(USER_EDIT_VIEW, model.asMap());
        }

        log.info("updating user profile for {}", username);
        Long id = this.userService.loadDuracloudUserByUsername(username).getId();
        this.userService.storeUserDetails(id, form.getFirstName(), form.getLastName(), form.getEmail(),
                form.getSecurityQuestion(), form.getSecurityAnswer(), form.getAllowableIPAddressRange());

        return new ModelAndView(formatUserRedirect(username));
    }

    @RequestMapping(value = { CHANGE_PASSWORD_MAPPING }, method = RequestMethod.GET)
    public String changePassword(@PathVariable String username, Model model) throws DBNotFoundException {
        log.debug("opening change password form  for {}", username);
        model.addAttribute(CHANGE_PASSWORD_FORM_KEY, new ChangePasswordForm());
        // add related model objects
        addUserToModel(this.userService.loadDuracloudUserByUsername(username), model);
        return CHANGE_PASSWORD_VIEW;
    }

    @Transactional
    @RequestMapping(value = { CHANGE_PASSWORD_MAPPING }, method = RequestMethod.POST)
    public ModelAndView changePassword(@PathVariable String username,
            @ModelAttribute(CHANGE_PASSWORD_FORM_KEY) @Valid ChangePasswordForm form, BindingResult result,
            Model model) throws Exception {

        DuracloudUser user = this.userService.loadDuracloudUserByUsername(username);

        // check for errors
        if (!result.hasErrors()) {
            log.info("changing user password for {}", username);
            Long id = user.getId();
            try {
                this.userService.changePassword(id, form.getOldPassword(), false, form.getPassword());
                return new ModelAndView(formatUserRedirect(username));
            } catch (InvalidPasswordException e) {
                result.addError(
                        new FieldError(CHANGE_PASSWORD_FORM_KEY, "oldPassword", "The old password is not correct"));
            }
        }

        log.debug("password form has errors for {}: returning...", username);
        addUserToModel(user, model);

        UserProfileEditForm editForm = new UserProfileEditForm();

        editForm.setFirstName(user.getFirstName());
        editForm.setLastName(user.getLastName());
        editForm.setEmail(user.getEmail());
        editForm.setSecurityQuestion(user.getSecurityQuestion());
        editForm.setSecurityAnswer(user.getSecurityAnswer());
        model.addAttribute(USER_PROFILE_FORM_KEY, editForm);
        return new ModelAndView(USER_EDIT_VIEW, model.asMap());

    }

    @RequestMapping(value = { "/change-password/{redemptionCode}" }, method = RequestMethod.GET)
    public String anonymousPasswordChange(@PathVariable String redemptionCode, Model model)
            throws DBNotFoundException {
        log.debug("opening change password form  for invitation {}", redemptionCode);
        if (checkRedemptionCode(redemptionCode, model) == null) {
            return ANONYMOUS_CHANGE_PASSWORD_FAILURE_VIEW;
        }
        model.addAttribute(CHANGE_PASSWORD_FORM_KEY, new AnonymousChangePasswordForm());
        return ANONYMOUS_CHANGE_PASSWORD_VIEW;
    }

    protected UserInvitation checkRedemptionCode(String redemptionCode, Model model) {
        try {
            return this.userService.retrievePassordChangeInvitation(redemptionCode);
        } catch (DBNotFoundException ex) {
            model.addAttribute(UserFeedbackUtil.FEEDBACK_KEY,
                    UserFeedbackUtil.create(Severity.ERROR, "This invitation is not valid."));
            return null;
        }
    }

    @Transactional
    @RequestMapping(value = { "/change-password/{redemptionCode}" }, method = RequestMethod.POST)
    public String anonymousPasswordChange(@PathVariable String redemptionCode,
            @ModelAttribute(CHANGE_PASSWORD_FORM_KEY) @Valid AnonymousChangePasswordForm form, BindingResult result,
            Model model) throws Exception {

        UserInvitation invitation = checkRedemptionCode(redemptionCode, model);
        if (invitation == null) {
            return ANONYMOUS_CHANGE_PASSWORD_FAILURE_VIEW;
        }

        String username = invitation.getAdminUsername();
        DuracloudUser user = this.userService.loadDuracloudUserByUsernameInternal(username);

        // check for errors
        if (!result.hasErrors()) {
            log.info("changing user password for {}", username);
            Long id = user.getId();
            try {
                this.userService.changePasswordInternal(id, user.getPassword(), true, form.getPassword());

                this.userService.redeemPasswordChangeRequest(user.getId(), redemptionCode);
                model.addAttribute("adminUrl", amaEndpoint.getUrl());
                return "anonymous-change-password-success";

            } catch (InvalidPasswordException e) {
                result.addError(
                        new FieldError(CHANGE_PASSWORD_FORM_KEY, "oldPassword", "The old password is not correct"));
            }
        }

        return ANONYMOUS_CHANGE_PASSWORD_VIEW;

    }

    /**
     * @param user
     * @param mav
     */
    private void prepareModel(DuracloudUser user, ModelAndView mav) {
        mav.addObject(USER_KEY, user);
        Set<AccountInfo> accounts = this.accountManagerService.findAccountsByUserId(user.getId());

        List<DuracloudAccount> activeAccounts = new ArrayList<>();
        List<DuracloudAccount> inactiveAccounts = new ArrayList<>();
        List<DuracloudAccount> pendingAccounts = new ArrayList<>();

        Iterator<AccountInfo> iterator = accounts.iterator();
        while (iterator.hasNext()) {
            AccountInfo acctInfo = iterator.next();
            DuracloudAccount duracloudAccount = loadAccountInstances(acctInfo, user);
            AccountStatus status = acctInfo.getStatus();
            if (AccountInfo.AccountStatus.ACTIVE.equals(status)) {
                activeAccounts.add(duracloudAccount);
            } else if (AccountInfo.AccountStatus.INACTIVE.equals(status)) {
                inactiveAccounts.add(duracloudAccount);
            } else if (AccountInfo.AccountStatus.PENDING.equals(status)) {
                pendingAccounts.add(duracloudAccount);
            }
        }
        Collections.sort(activeAccounts);
        Collections.sort(inactiveAccounts);
        Collections.sort(pendingAccounts);
        mav.addObject("activeAccounts", activeAccounts);
        mav.addObject("inactiveAccounts", inactiveAccounts);
        mav.addObject("pendingAccounts", pendingAccounts);
    }

    private DuracloudAccount loadAccountInstances(AccountInfo accountInfo, DuracloudUser user) {
        DuracloudAccount duracloudAccount = new DuracloudAccount();
        duracloudAccount.setAccountInfo(accountInfo);
        duracloudAccount.setUserRole(user.getRoleByAcct(accountInfo.getId()));
        return duracloudAccount;
    }

    private void addUserToModel(DuracloudUser user, Model model) {
        model.addAttribute(USER_KEY, user);
    }

    /**
     * @param mav
     */
    private void prepareModel(String username, ModelAndView mav) {

        DuracloudUser user;
        try {
            user = this.userService.loadDuracloudUserByUsernameInternal(username);
            prepareModel(user, mav);
        } catch (DBNotFoundException e) {
            throw new AccessDeniedException("Access is denied", e);
        }
    }

    @Transactional
    @RequestMapping(value = { NEW_MAPPING }, method = RequestMethod.POST)
    public ModelAndView add(@ModelAttribute(NEW_USER_FORM_KEY) @Valid NewUserForm newUserForm, BindingResult result,
            Model model, RedirectAttributes redirectAttributes) throws Exception {

        String name = null == newUserForm ? "null" : newUserForm.getUsername();
        log.debug("Add new user: {}", name);
        if (result.hasErrors()) {
            return new ModelAndView(NEW_USER_VIEW, model.asMap());
        }

        DuracloudUser user = this.userService.createNewUser(newUserForm.getUsername(), newUserForm.getPassword(),
                newUserForm.getFirstName(), newUserForm.getLastName(), newUserForm.getEmail(),
                newUserForm.getSecurityQuestion(), newUserForm.getSecurityAnswer());

        String redemptionCode = newUserForm.getRedemptionCode();
        Long accountId = -1L;
        if (!StringUtils.isEmpty(redemptionCode)) {
            try {
                accountId = this.userService.redeemAccountInvitation(user.getId(), redemptionCode);

            } catch (InvalidRedemptionCodeException ex) {
                addRedemptionFailedMessage();
            }
        }

        String userUrl = formatUserUrl(newUserForm.getUsername());

        if (accountId > -1) {
            userUrl += "?accountId=" + accountId;
        }

        ModelAndView mav = new ModelAndView("user-new-success");
        mav.addObject("userUrl", userUrl);
        return mav;
    }

    protected static RedirectView formatUserRedirect(String username) {
        String redirect = formatUserUrl(username);
        RedirectView view = new RedirectView(redirect, true);
        view.setExposeModelAttributes(false);
        return view;
    }

    @Transactional
    @RequestMapping(value = { FORGOT_PASSWORD_MAPPING }, method = RequestMethod.POST)
    public String forgotPassword(
            @ModelAttribute(FORGOT_PASSWORD_FORM_KEY) @Valid ForgotPasswordForm forgotPasswordForm,
            BindingResult result, Model model, HttpServletRequest request) throws Exception {

        model.addAttribute(FORGOT_PASSWORD_FORM_KEY, forgotPasswordForm);

        if (!result.hasErrors()) {

            try {

                String username = forgotPasswordForm.getUsername();

                if (StringUtils.isEmpty(forgotPasswordForm.getSecurityQuestion())) {
                    DuracloudUser user = this.userService.loadDuracloudUserByUsernameInternal(username);
                    forgotPasswordForm.setSecurityQuestion(user.getSecurityQuestion());
                }

                if (StringUtils.isEmpty(forgotPasswordForm.getSecurityAnswer())) {
                    return FORGOT_PASSWORD_VIEW;
                }

                this.userService.forgotPassword(username, forgotPasswordForm.getSecurityQuestion(),
                        forgotPasswordForm.getSecurityAnswer());
            } catch (DBNotFoundException e) {
                result.addError(
                        new FieldError(FORGOT_PASSWORD_FORM_KEY, "username", "The username does not exist"));
                return FORGOT_PASSWORD_VIEW;
            } catch (InvalidPasswordException e) {
                result.addError(new FieldError(FORGOT_PASSWORD_FORM_KEY, "securityQuestion",
                        "The security answer is not correct"));
                return FORGOT_PASSWORD_VIEW;
            } catch (UnsentEmailException ue) {
                result.addError(new ObjectError(FORGOT_PASSWORD_FORM_KEY,
                        "Unable to send email to the address associated with the username"));
                return FORGOT_PASSWORD_VIEW;
            }

            return FORGOT_PASSWORD_SUCCESS_VIEW;
        }

        return FORGOT_PASSWORD_VIEW;
    }

    /**
     * @param string
     */
    private void addErrorMessage(String string) {
        // TODO Auto-generated method stub

    }

    @RequestMapping(value = { "/redeem/{redemptionCode}" }, method = RequestMethod.GET)
    public ModelAndView redeemUser(HttpServletRequest request, @PathVariable String redemptionCode)
            throws DBNotFoundException {
        log.info("getting redeem invitation {}", redemptionCode);

        // force logout
        request.getSession().invalidate();
        SecurityContextHolder.clearContext();

        // add the redemption code to the session
        request.getSession(true).setAttribute("redemptionCode", redemptionCode);
        ModelAndView mav = new ModelAndView(HomeController.HOME_VIEW_ID);
        mav.addObject("redemptionCode", redemptionCode);
        return mav;
    }

    public void setAccountManagerService(AccountManagerService accountManagerService) {
        this.accountManagerService = accountManagerService;
    }

    public AccountManagerService getAccountManagerService() {
        return accountManagerService;
    }

    public void setAmaEndpoint(AmaEndpoint amaEndpoint) {
        this.amaEndpoint = amaEndpoint;
    }
}