com.jd.survey.web.security.LoginController.java Source code

Java tutorial

Introduction

Here is the source code for com.jd.survey.web.security.LoginController.java

Source

/*Copyright (C) 2014  JD Software, Inc.
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
    
This program 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 Affero General Public License for more details.
    
You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.jd.survey.web.security;

import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.routines.DateValidator;
import org.apache.util.Base64;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
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.bind.annotation.RequestParam;
import org.springframework.web.util.UriUtils;
import org.springframework.web.util.WebUtils;

import com.jd.survey.GlobalSettings;
import com.jd.survey.domain.security.PasswordResetRequest;
import com.jd.survey.domain.security.User;
import com.jd.survey.domain.settings.Invitation;
import com.jd.survey.service.email.MailService;
import com.jd.survey.service.security.UserService;
import com.jd.survey.service.settings.ApplicationSettingsService;
import com.jd.survey.service.settings.SurveySettingsService;

@RequestMapping("/public")
@Controller
public class LoginController {
    private static final Log log = LogFactory.getLog(LoginController.class);
    private static final String DATE_FORMAT = "date_format";

    @Autowired
    private UserService userService;
    @Autowired
    private MailService mailService;
    @Autowired
    private VelocityEngine velocityEngine;
    @Autowired
    private MessageSource messageSource;
    @Autowired
    private SurveySettingsService surveySettingsService;
    @Autowired
    private ApplicationSettingsService applicationSettingsService;

    private static final long FORGOT_LOGIN_VELOCITY_EMAIL_TEMPLATE_ID = 1;
    private static final long FORGOT_PASSWORD_VELOCITY_EMAIL_TEMPLATE_ID = 2;

    private static final String LOGIN_PARAMETER_NAME = "login_parameter_name";
    private static final String RESET_PASSWORD_LINK_PARAMETER_NAME = "reset_password_link_parameter_name";
    private static final String RESET_PASSWORD_LINK_LABEL = "reset_password_link_label";
    //private static final String INTERNAL_SITE_BASE_URL="internal_site_base_url";
    //private static final String EXTERNAL_SITE_BASE_URL="external_site_base_url";
    @Value("${external.base.url}")
    String externalBaseUrl;
    @Value("${internal.base.url}")
    String internalBaseUrl;

    private static final String FORGOT_LOGIN_EMAIL_TITLE = "forgot_login_email_title";
    private static final String FORGOT_PASSWORD_EMAIL_TITLE = "forgot_password_email_title";

    @RequestMapping(method = RequestMethod.GET, value = "/w/{uuid}", produces = "image/gif")
    public void getWhiteGif(@PathVariable("uuid") String uuid, Principal principal,
            HttpServletRequest httpServletRequest, HttpServletResponse response) {

        try {

            Invitation invitation = surveySettingsService.invitation_findByUuid(uuid);
            if (invitation != null) {
                surveySettingsService.invitation_updateAsRead(invitation.getId());
            }

            //white 1 x 1 pixel gif binary 
            byte[] trackingGif = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x1, 0x0, 0x1, 0x0, (byte) 0x80, 0x0, 0x0,
                    (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1,
                    0x0, 0x0, 0x2, 0x2, 0x44, 0x1, 0x0, 0x3b };

            response.setContentType("image/gif");
            ServletOutputStream servletOutputStream = response.getOutputStream();
            servletOutputStream.write(trackingGif);
            servletOutputStream.flush();

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @RequestMapping(method = RequestMethod.GET, value = "/", params = "flogin", produces = "text/html")
    public String forgotLoginGet(Model uiModel, HttpServletRequest httpServletRequest) {
        try {
            uiModel.addAttribute("status", "N");
            return "public/flogin";
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @RequestMapping(method = RequestMethod.POST, value = "/", params = "flogin", produces = "text/html")
    public String forgotLoginPost(@RequestParam(value = "email", required = true) String email,
            @RequestParam(value = "_proceed", required = false) String proceed, Model uiModel,
            HttpServletRequest httpServletRequest) {
        try {
            log.info("post");
            if (proceed != null) { //Proceed
                User user = userService.user_findByEmail(email);
                if (user != null) {
                    StringWriter sw = new StringWriter();
                    Map model = new HashMap();
                    model.put(messageSource.getMessage(LOGIN_PARAMETER_NAME, null, LocaleContextHolder.getLocale())
                            .replace("${", "").replace("}", ""), user.getLogin());
                    VelocityContext velocityContext = new VelocityContext(model);
                    Velocity.evaluate(velocityContext, sw, "velocity-log", surveySettingsService
                            .velocityTemplate_findById(FORGOT_LOGIN_VELOCITY_EMAIL_TEMPLATE_ID).getDefinition());

                    mailService.sendEmail(email, messageSource.getMessage(FORGOT_LOGIN_EMAIL_TITLE, null,
                            LocaleContextHolder.getLocale()), sw.toString().trim());
                    uiModel.addAttribute("status", "S");
                    return "public/flogin";
                } else {
                    log.info("no match");
                    uiModel.addAttribute("status", "I");
                    return "public/flogin";
                }
            } else { //Cancel button

                return "public/login";
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @RequestMapping(method = RequestMethod.GET, value = "/", params = "fpass", produces = "text/html")
    public String forgotPasswordGet(Model uiModel, HttpServletRequest httpServletRequest) {
        try {
            uiModel.addAttribute("dateFormat",
                    messageSource.getMessage(DATE_FORMAT, null, LocaleContextHolder.getLocale()));
            uiModel.addAttribute("status", "N");
            return "public/fpass";
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @SuppressWarnings("unchecked")
    @RequestMapping(method = RequestMethod.POST, value = "/", params = "fpass", produces = "text/html")
    public String forgotPasswordPost(@RequestParam(value = "login", required = true) String login,
            @RequestParam(value = "dob", required = true) String dob,
            @RequestParam(value = "_proceed", required = false) String proceed, Model uiModel,
            HttpServletRequest httpServletRequest) {
        try {
            if (proceed != null) {
                String resetPasswordLink;
                String dateFormat = messageSource.getMessage(DATE_FORMAT, null, LocaleContextHolder.getLocale());

                //Validate date and login entries (sanitize) 
                if (login == null || login.isEmpty() || login.length() > 100 || dob == null || dob.isEmpty()
                        || !GenericValidator.isDate(dob, dateFormat, true)) {
                    uiModel.addAttribute("status", "I");
                    return "public/fpass";
                }

                //Check if provided DOB and login match
                if (!userService.user_validateDateofBirthAndLogin(login,
                        DateValidator.getInstance().validate(dob))) {
                    uiModel.addAttribute("status", "I");
                    return "public/fpass";
                }

                User user = userService.user_findByLogin(login);
                if (httpServletRequest.getRequestURI().contains("external")) {
                    //resetPasswordLink =messageSource.getMessage(EXTERNAL_SITE_BASE_URL, null, LocaleContextHolder.getLocale());
                    resetPasswordLink = externalBaseUrl;
                } else {
                    //resetPasswordLink =messageSource.getMessage(INTERNAL_SITE_BASE_URL, null, LocaleContextHolder.getLocale());
                    resetPasswordLink = internalBaseUrl;
                }
                if (resetPasswordLink.endsWith("/")) {
                    resetPasswordLink = resetPasswordLink + "public/rpass?key=";
                } else {
                    resetPasswordLink = resetPasswordLink + "/public/rpass?key=";
                }

                StringWriter sw = new StringWriter();
                Map model = new HashMap();
                model.put(
                        messageSource.getMessage(RESET_PASSWORD_LINK_PARAMETER_NAME, null,
                                LocaleContextHolder.getLocale()).replace("${", "").replace("}", ""),
                        "<a href='" + resetPasswordLink
                                + userService.user_prepareForgotPasswordMessage(user.getId()) + "'>"
                                + messageSource.getMessage(RESET_PASSWORD_LINK_LABEL, null,
                                        LocaleContextHolder.getLocale())
                                + "</a>");
                VelocityContext velocityContext = new VelocityContext(model);
                Velocity.evaluate(velocityContext, sw, "velocity-log", surveySettingsService
                        .velocityTemplate_findById(FORGOT_PASSWORD_VELOCITY_EMAIL_TEMPLATE_ID).getDefinition());

                mailService.sendEmail(user.getEmail(), messageSource.getMessage(FORGOT_PASSWORD_EMAIL_TITLE, null,
                        LocaleContextHolder.getLocale()), sw.toString());
                uiModel.addAttribute("status", "S");
                return "public/fpass";
            } else { //cancel button
                return "public/login";
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @RequestMapping(method = RequestMethod.GET, value = "/rpass", produces = "text/html")
    public String forgotPasswordGet(@RequestParam(value = "key", required = true) String key, Model uiModel,
            HttpServletRequest httpServletRequest) {
        try {
            if (userService.user_validateForgotPasswordKey(key)) {
                uiModel.addAttribute("key", key);
                uiModel.addAttribute("status", "v");
                return "public/rpass";
            } else {
                log.warn("Attempt to reset password with invalid key, Not successful");
                uiModel.addAttribute("status", "E");//Error
                throw (new RuntimeException("Attempt to reset password with invalid key, Not successful"));
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @RequestMapping(method = RequestMethod.POST, value = "/rpass", produces = "text/html")
    public String forgotPasswordPost(@RequestParam(value = "password", required = true) String password,
            @RequestParam(value = "cpassword", required = true) String cpassword,
            @RequestParam(value = "key", required = true) String key,
            @RequestParam(value = "_proceed", required = false) String proceed, Model uiModel,
            HttpServletRequest httpServletRequest) {
        try {
            if (proceed != null) {
                //validate the passed key
                if (!userService.user_validateForgotPasswordKey(key)) {
                    log.warn("Attempt to reset password with invalid key, Not successful");
                    uiModel.addAttribute("status", "E"); //Error
                    throw (new RuntimeException("Attempt to reset password with invalid key, Not successful"));
                }

                //check that passwords match    
                if (!password.equals(cpassword)) {
                    uiModel.asMap().clear();
                    uiModel.addAttribute("key", key);
                    uiModel.addAttribute("status", "U"); //Unmatching Passwords
                    return "public/rpass";
                }

                GlobalSettings globalSettings = applicationSettingsService.getSettings();

                //Check new password strength 
                if (!GenericValidator.matchRegexp(password, globalSettings.getPasswordEnforcementRegex())) {
                    uiModel.asMap().clear();
                    uiModel.addAttribute("key", key);
                    uiModel.addAttribute("status", "I"); //Unmatching Passwords
                    uiModel.addAttribute("passwordPolicyMsg", globalSettings.getPasswordEnforcementMessage());
                    return "public/rpass";
                }

                //All validations passed, save the HASH of the password in the database
                PasswordResetRequest passwordResetRequest = userService.passwordResetRequest_findByHash(key);
                User user = userService.user_findByLogin(passwordResetRequest.getLogin());
                user.setPassword(password);
                userService.user_updatePassword(user, passwordResetRequest);
                uiModel.addAttribute("status", "S");//success
                return "public/rpass";
            } else {
                //cancel button
                return "public/login";
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    String encodeUrlPathSegment(String pathSegment, HttpServletRequest httpServletRequest) {
        log.info("encodeUrlPathSegment()");
        try {
            String enc = httpServletRequest.getCharacterEncoding();
            if (enc == null) {
                enc = WebUtils.DEFAULT_CHARACTER_ENCODING;
            }
            try {
                pathSegment = UriUtils.encodePathSegment(pathSegment, enc);
            } catch (UnsupportedEncodingException uee) {
                log.error(uee);
            }
            return pathSegment;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw (new RuntimeException(e));
        }
    }

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true, 10));
    }

    @ExceptionHandler(RuntimeException.class)
    public String handleRuntimeException(RuntimeException ex, HttpServletRequest request) {
        log.error("handling RuntimeException");
        log.error("redirect to /err");
        return "redirect:/err";
    }

}