org.cloudfoundry.identity.uaa.account.ResetPasswordController.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudfoundry.identity.uaa.account.ResetPasswordController.java

Source

/*******************************************************************************
 *     Cloud Foundry
 *     Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
 *
 *     This product is licensed to you under the Apache License, Version 2.0 (the "License").
 *     You may not use this product except in compliance with the License.
 *
 *     This product includes a number of subcomponents with
 *     separate copyright notices and license terms. Your use of these
 *     subcomponents is subject to the terms and conditions of the
 *     subcomponent's license, as noted in the LICENSE file.
 *******************************************************************************/
package org.cloudfoundry.identity.uaa.account;

import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.codestore.ExpiringCode;
import org.cloudfoundry.identity.uaa.codestore.ExpiringCodeStore;
import org.cloudfoundry.identity.uaa.login.AccountSavingAuthenticationSuccessHandler;
import org.cloudfoundry.identity.uaa.message.MessageService;
import org.cloudfoundry.identity.uaa.message.MessageType;
import org.cloudfoundry.identity.uaa.scim.endpoints.PasswordChange;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.user.UaaUserDatabase;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.UaaUrlUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.sql.Timestamp;
import java.util.Map;
import java.util.regex.Pattern;

import static org.springframework.util.StringUtils.hasText;

@Controller
public class ResetPasswordController {
    protected final Log logger = LogFactory.getLog(getClass());

    private final ResetPasswordService resetPasswordService;
    private final MessageService messageService;
    private final TemplateEngine templateEngine;
    private final Pattern emailPattern;
    private final ExpiringCodeStore codeStore;
    private final UaaUserDatabase userDatabase;
    private final AccountSavingAuthenticationSuccessHandler successHandler;

    public ResetPasswordController(ResetPasswordService resetPasswordService, MessageService messageService,
            TemplateEngine templateEngine, ExpiringCodeStore codeStore, UaaUserDatabase userDatabase,
            AccountSavingAuthenticationSuccessHandler successHandler) {
        this.resetPasswordService = resetPasswordService;
        this.messageService = messageService;
        this.templateEngine = templateEngine;
        this.successHandler = successHandler;
        emailPattern = Pattern.compile("^\\S+@\\S+\\.\\S+$");
        this.codeStore = codeStore;
        this.userDatabase = userDatabase;
    }

    @RequestMapping(value = "/forgot_password", method = RequestMethod.GET)
    public String forgotPasswordPage(Model model,
            @RequestParam(required = false, value = "client_id") String clientId,
            @RequestParam(required = false, value = "redirect_uri") String redirectUri) {
        model.addAttribute("client_id", clientId);
        model.addAttribute("redirect_uri", redirectUri);
        return "forgot_password";
    }

    @RequestMapping(value = "/forgot_password.do", method = RequestMethod.POST)
    public String forgotPassword(Model model, @RequestParam("email") String email,
            @RequestParam(value = "client_id", defaultValue = "") String clientId,
            @RequestParam(value = "redirect_uri", defaultValue = "") String redirectUri,
            HttpServletResponse response) {
        if (emailPattern.matcher(email).matches()) {
            forgotPassword(email, clientId, redirectUri);
            return "redirect:email_sent?code=reset_password";
        } else {
            return handleUnprocessableEntity(model, response, "message_code", "form_error");
        }
    }

    private void forgotPassword(String email, String clientId, String redirectUri) {
        String subject = getSubjectText();
        String htmlContent = null;
        String userId = null;

        try {
            ForgotPasswordInfo forgotPasswordInfo = resetPasswordService.forgotPassword(email, clientId,
                    redirectUri);
            userId = forgotPasswordInfo.getUserId();
            htmlContent = getCodeSentEmailHtml(forgotPasswordInfo.getResetPasswordCode().getCode());
        } catch (ConflictException e) {
            htmlContent = getResetUnavailableEmailHtml(email);
            userId = e.getUserId();
        } catch (NotFoundException e) {
            logger.error("User with email address " + email + " not found.");
        }

        if (htmlContent != null && userId != null) {
            messageService.sendMessage(email, MessageType.PASSWORD_RESET, subject, htmlContent);
        }
    }

    private String getSubjectText() {
        String serviceName = getServiceName();
        if (StringUtils.isEmpty(serviceName)) {
            return "Account password reset request";
        }
        return serviceName + " account password reset request";
    }

    private String getCodeSentEmailHtml(String code) {
        String resetUrl = UaaUrlUtils.getUaaUrl("/reset_password");

        final Context ctx = new Context();
        ctx.setVariable("serviceName", getServiceName());
        ctx.setVariable("code", code);
        ctx.setVariable("resetUrl", resetUrl);
        return templateEngine.process("reset_password", ctx);
    }

    private String getResetUnavailableEmailHtml(String email) {
        String hostname = UaaUrlUtils.getUaaHost();

        final Context ctx = new Context();
        ctx.setVariable("serviceName", getServiceName());
        ctx.setVariable("email", email);
        ctx.setVariable("hostname", hostname);
        return templateEngine.process("reset_password_unavailable", ctx);
    }

    private String getServiceName() {
        if (IdentityZoneHolder.get().equals(IdentityZone.getUaa())) {
            String companyName = IdentityZoneHolder.resolveBranding().getCompanyName();
            return StringUtils.hasText(companyName) ? companyName : "Cloud Foundry";
        } else {
            return IdentityZoneHolder.get().getName();
        }
    }

    @RequestMapping(value = "/email_sent", method = RequestMethod.GET)
    public String emailSentPage(@ModelAttribute("code") String code) {
        return "email_sent";
    }

    @RequestMapping(value = "/reset_password", method = RequestMethod.GET, params = { "code" })
    public String resetPasswordPage(Model model, HttpServletResponse response, @RequestParam("code") String code) {

        ExpiringCode expiringCode = checkIfUserExists(codeStore.retrieveCode(code));
        if (expiringCode == null) {
            return handleUnprocessableEntity(model, response, "message_code", "bad_code");
        } else {
            PasswordChange passwordChange = JsonUtils.readValue(expiringCode.getData(), PasswordChange.class);
            String userId = passwordChange.getUserId();
            UaaUser uaaUser = userDatabase.retrieveUserById(userId);
            String newCode = codeStore
                    .generateCode(expiringCode.getData(),
                            new Timestamp(System.currentTimeMillis() + (10 * 60 * 1000)), expiringCode.getIntent())
                    .getCode();
            model.addAttribute("code", newCode);
            model.addAttribute("email", uaaUser.getEmail());
            model.addAttribute("passwordPolicy", resetPasswordService.getPasswordPolicy());
            return "reset_password";
        }
    }

    public ExpiringCode checkIfUserExists(ExpiringCode code) {
        if (code == null) {
            logger.debug("reset_password ExpiringCode object is null. Aborting.");
            return null;
        }
        if (!hasText(code.getData())) {
            logger.debug(
                    "reset_password ExpiringCode[" + code.getCode() + "] data string is null or empty. Aborting.");
            return null;
        }
        Map<String, String> data = JsonUtils.readValue(code.getData(), new TypeReference<Map<String, String>>() {
        });
        if (!hasText(data.get("user_id"))) {
            logger.debug("reset_password ExpiringCode[" + code.getCode()
                    + "] user_id string is null or empty. Aborting.");
            return null;
        }
        String userId = data.get("user_id");
        try {
            userDatabase.retrieveUserById(userId);
        } catch (UsernameNotFoundException e) {
            logger.debug("reset_password ExpiringCode[" + code.getCode() + "] user_id is invalid. Aborting.");
            return null;
        }
        return code;
    }

    @RequestMapping(value = "/reset_password.do", method = RequestMethod.POST)
    public void resetPassword(Model model, @RequestParam("code") String code, @RequestParam("email") String email,
            @RequestParam("password") String password,
            @RequestParam("password_confirmation") String passwordConfirmation, HttpServletRequest request,
            HttpServletResponse response, HttpSession session) {

    }

    private String handleUnprocessableEntity(Model model, HttpServletResponse response, String attributeKey,
            String attributeValue) {
        model.addAttribute(attributeKey, attributeValue);
        response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
        return "forgot_password";
    }
}