Java tutorial
/* * Copyright 2013 Citrix Systems, Inc. You may not use, copy, or modify this * file except pursuant to a valid license agreement from Citrix Systems, Inc. */ package com.citrix.cpbm.portal.fragment.controllers; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.PrivilegedAction; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.LockedException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.WebAttributes; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.ui.ModelMap; 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.bind.annotation.ResponseBody; import org.springframework.web.util.HtmlUtils; import com.citrix.cpbm.platform.admin.service.ConnectorManagementService; import com.citrix.cpbm.platform.spi.CloudConnectorFactory.ConnectorType; import com.vmops.internal.service.EventService; import com.vmops.internal.service.PrivilegeService; import com.vmops.internal.service.TelephoneVerificationService; import com.vmops.model.Event.Category; import com.vmops.model.Event.Scope; import com.vmops.model.Event.Severity; import com.vmops.model.Event.Source; import com.vmops.model.User; import com.vmops.model.UserAlertPreferences; import com.vmops.model.UserAlertPreferences.AlertType; import com.vmops.portal.config.Configuration; import com.vmops.portal.config.Configuration.Names; import com.vmops.service.ChannelService; import com.vmops.service.SupportService; import com.vmops.service.TenantService; import com.vmops.service.UserAlertPreferencesService; import com.vmops.service.UserService; import com.vmops.service.exceptions.NoSuchUserException; import com.vmops.service.exceptions.TelephoneVerificationServiceException; import com.vmops.utils.CryptoUtils; import com.vmops.web.controllers.AbstractBaseController; import com.vmops.web.filters.CaptchaAuthenticationFilter; import com.vmops.web.filters.exceptions.CaptchaValidationException; import com.vmops.web.filters.exceptions.IpRangeValidationException; public abstract class AbstractAuthenticationController extends AbstractBaseController { /** * The parameter name that indicates login failed. */ private static final String LOGIN_FAILED_PARAM = "login_failed"; private static final String LOGOUT_PARAM = "logout"; private static final String TIME_OUT = "timeout"; private static final String VERIFY = "verify"; private static final String CAS = "cas"; @Autowired private TenantService tenantService; @Autowired private UserService userService; @Autowired private PrivilegeService privilegeService; @Autowired private EventService eventService; @Autowired protected ConnectorManagementService connectorManagementService; @Autowired private UserAlertPreferencesService userAlertPreferencesService; @Autowired private SupportService supportService; @Autowired private ChannelService channelService; /** * Logger. */ private static Logger logger = Logger.getLogger(AbstractAuthenticationController.class); @RequestMapping({ "/login", "/portal", "/" }) public String login(HttpServletRequest request, ModelMap map, HttpSession session) { logger.debug("###Entering in login(req,map,session) method"); boolean loginFailed = request.getParameter(LOGIN_FAILED_PARAM) != null; if (!loginFailed && request.getUserPrincipal() != null) { map.clear(); return "redirect:/portal/home"; } if (session.getAttribute("email_verified") != null) { map.addAttribute("email_verified", session.getAttribute("email_verified")); session.removeAttribute("email_verified"); } String showSuffixControl = "false"; String suffixControlType = "textbox"; List<String> suffixList = null; if (config.getValue(Names.com_citrix_cpbm_username_duplicate_allowed).equals("true")) { showSuffixControl = "true"; if (config.getValue(Names.com_citrix_cpbm_login_screen_tenant_suffix_dropdown_enabled).equals("true")) { suffixControlType = "dropdown"; suffixList = tenantService.getSuffixList(); } } map.addAttribute("showSuffixControl", showSuffixControl); map.addAttribute("suffixControlType", suffixControlType); map.addAttribute("suffixList", suffixList); if (config.getBooleanValue(Configuration.Names.com_citrix_cpbm_portal_directory_service_enabled) && config.getValue(Names.com_citrix_cpbm_directory_mode).equals("pull")) { map.addAttribute("directoryServiceAuthenticationEnabled", "true"); } if (config.getValue(Names.com_citrix_cpbm_public_catalog_display).equals("true") && channelService.getDefaultServiceProviderChannel() != null) { map.addAttribute("showAnonymousCatalogBrowsing", "true"); } map.addAttribute("showLanguageSelection", "true"); map.addAttribute("supportedLocaleList", this.getLocaleDisplayName(listSupportedLocales())); map.addAttribute("selected_language", request.getParameter("lang")); String redirect = null; boolean loggedOut = request.getParameter(LOGOUT_PARAM) != null; final Throwable ex = (Throwable) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION); // capture previous CAPTCHA position Boolean captchaRequiredSessionObj = (Boolean) session .getAttribute(CaptchaAuthenticationFilter.CAPTCHA_REQUIRED); // Get last user String username = (String) session .getAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY); // this as spring does a text-escape when it saves this attribute final String uUsername = HtmlUtils.htmlUnescape(username); if (loginFailed) { String error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); try { User user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.getUserByParam("username", uUsername, false); // All user writes here. // Every time there is a login failure but not invalid CAPTCHA, // we update failed login attempts for the user if (!(ex instanceof CaptchaValidationException) && !(ex instanceof LockedException) && !(ex instanceof IpRangeValidationException)) { user.setFailedLoginAttempts(user.getFailedLoginAttempts() + 1); } int attempts = user.getFailedLoginAttempts(); // Also locking the root user and quite easily too. Clearly this // needs an eye! if (attempts >= config.getIntValue( Names.com_citrix_cpbm_accountManagement_security_logins_lockThreshold)) { user.setEnabled(false); } return user; } }); int attempts = user.getFailedLoginAttempts(); if (attempts >= config .getIntValue(Names.com_citrix_cpbm_accountManagement_security_logins_captchaThreshold)) { session.setAttribute(CaptchaAuthenticationFilter.CAPTCHA_REQUIRED, true); } } catch (NoSuchUserException e) { // map.addAttribute("showCaptcha", true); } captchaRequiredSessionObj = (Boolean) session .getAttribute(CaptchaAuthenticationFilter.CAPTCHA_REQUIRED); map.addAttribute("loginFailed", loginFailed); String lastUsername = uUsername; if (config.getValue(Names.com_citrix_cpbm_username_duplicate_allowed).equals("true")) { if (!lastUsername.equals("root") && !lastUsername.equals("")) { lastUsername = lastUsername.substring(0, lastUsername.lastIndexOf('@')); } } map.addAttribute("lastUser", lastUsername); // Compose error string if (ex instanceof DisabledException) { error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); } else if (ex instanceof CaptchaValidationException) { error = " " + messageSource.getMessage("error.auth.captcha.invalid", null, request.getLocale()); } else if (ex instanceof IpRangeValidationException) { error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); } else if (ex instanceof LockedException) { error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); } else if (ex instanceof BadCredentialsException) { if (ex.getMessage() != null && ex.getMessage().length() > 0) { // error = " " + ex.getMessage(); error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); } } else if (ex instanceof AuthenticationException) { error = " " + messageSource.getMessage("error.auth.username.password.invalid", null, request.getLocale()); } else { logger.error("Error occurred in authentication", ex); error = " " + messageSource.getMessage("error.auth.unknown", null, request.getLocale()); } if (captchaRequiredSessionObj != null && captchaRequiredSessionObj == true && !(ex instanceof CaptchaValidationException) && !(ex instanceof LockedException)) { error += " " + messageSource.getMessage("error.auth.account.may.locked", null, request.getLocale()); } map.addAttribute("error", error); } if (loggedOut) { map.addAttribute("logout", loggedOut); } // This could come from session or from user if (captchaRequiredSessionObj != null && captchaRequiredSessionObj.booleanValue() && !Boolean.valueOf(config.getValue(Names.com_citrix_cpbm_use_intranet_only))) { map.addAttribute("showCaptcha", true); map.addAttribute("recaptchaPublicKey", config.getRecaptchaPublicKey()); } map.addAttribute(TIME_OUT, request.getParameter(TIME_OUT) != null); map.addAttribute(VERIFY, request.getParameter(VERIFY) != null); logger.debug("###Exiting login(req,map,session) method"); if (config.getAuthenticationService().compareToIgnoreCase(CAS) == 0) { try { redirect = StringUtils.isEmpty(config.getCasLoginUrl()) ? null : config.getCasLoginUrl() + "?service=" + URLEncoder.encode(config.getCasServiceUrl(), "UTF-8"); } catch (UnsupportedEncodingException e) { logger.error("Exception encoding: " + redirect, e); } if (redirect == null) { throw new InternalError("CAS authentication required, but login url not set"); } } return redirect == null ? "auth.login" : "redirect:" + redirect; } @RequestMapping(value = { "/{userParam}/loggedout", "{userParam}/j_spring_security_logout" }) public String loggedout(@PathVariable String userParam, ModelMap map, HttpSession session, HttpServletResponse response, HttpServletRequest request) { logger.debug("###Entering in loggedout(response) method"); String showSuffixControl = "false"; String suffixControlType = "textbox"; List<String> suffixList = null; if (config.getValue(Names.com_citrix_cpbm_username_duplicate_allowed).equals("true")) { showSuffixControl = "true"; if (config.getValue(Names.com_citrix_cpbm_login_screen_tenant_suffix_dropdown_enabled).equals("true")) { suffixControlType = "dropdown"; suffixList = tenantService.getSuffixList(); } } map.addAttribute("showSuffixControl", showSuffixControl); map.addAttribute("suffixControlType", suffixControlType); map.addAttribute("suffixList", suffixList); if (config.getBooleanValue(Configuration.Names.com_citrix_cpbm_portal_directory_service_enabled) && config.getValue(Names.com_citrix_cpbm_directory_mode).equals("pull")) { map.addAttribute("directoryServiceAuthenticationEnabled", "true"); } if (config.getValue(Names.com_citrix_cpbm_public_catalog_display).equals("true") && channelService.getDefaultServiceProviderChannel() != null) { map.addAttribute("showAnonymousCatalogBrowsing", "true"); } map.addAttribute("showLanguageSelection", "true"); map.addAttribute("supportedLocaleList", this.getLocaleDisplayName(listSupportedLocales())); map.addAttribute("logout", true); String redirect = null; Enumeration<String> en = session.getAttributeNames(); while (en.hasMoreElements()) { String attr = en.nextElement(); session.removeAttribute(attr); } Cookie cookie = new Cookie("JforumSSO", ""); cookie.setMaxAge(0); cookie.setPath("/"); response.addCookie(cookie); if (request.getRequestedSessionId() != null && request.isRequestedSessionIdValid()) { // create logout notification begins User user = userService.get(userParam); String message = "logged.out"; String messageArgs = user.getUsername(); eventService.createEvent(new Date(), user, message, messageArgs, Source.PORTAL, Scope.USER, Category.ACCOUNT, Severity.INFORMATION, true); } session.invalidate(); if (config.getAuthenticationService().compareToIgnoreCase(CAS) == 0) { try { redirect = StringUtils.isEmpty(config.getCasLogoutUrl()) ? null : config.getCasLogoutUrl() + "?service=" + URLEncoder.encode(config.getCasServiceUrl(), "UTF-8"); } catch (UnsupportedEncodingException e) { logger.error("Exception encoding: " + redirect, e); } if (redirect == null) { throw new InternalError("CAS authentication required, but login url not set"); } } SecurityContextHolder.getContext().setAuthentication(null); // ends logger.debug("###Exiting loggedout(response) method"); return redirect == null ? "redirect:/j_spring_security_logout" : "redirect:" + redirect; } @RequestMapping(value = "/reset_password", method = RequestMethod.GET) public String requestReset(ModelMap map) { String showSuffixControl = "false"; if (config.getValue(Names.com_citrix_cpbm_username_duplicate_allowed).equals("true")) { showSuffixControl = "true"; map.addAttribute("useSmallCss", "true"); } map.addAttribute("showSuffixControl", showSuffixControl); return "auth.request_reset"; } /** * @param userName * @param pickValFromReset * @param request * @return * @throws JsonGenerationException * @throws JsonMappingException * @throws IOException */ @RequestMapping(value = "/request_call_by_user", method = RequestMethod.POST) @ResponseBody public Map<String, String> requestCall( @RequestParam(value = "userName", required = false) final String userName, @RequestParam(value = "pickValFromReset", required = false) final String pickValFromReset, HttpServletRequest request) throws JsonGenerationException, JsonMappingException, IOException { Map<String, String> returnResponse = new HashMap<String, String>(); String generatedPhoneVerificationPin = request.getSession().getAttribute("phoneVerificationPin").toString(); final String userNameLoc; if (pickValFromReset != null && pickValFromReset.equals("pick")) { userNameLoc = (String) request.getSession().getAttribute(RESET_USER_KEY); } else if (userName != null) { userNameLoc = userName; } else { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.callFailed", null, request.getLocale())); return returnResponse; } request.getSession().setAttribute("userName", userNameLoc); User user = null; try { user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.getUserByParam("username", userNameLoc, false); return user; } }); } catch (NoSuchUserException e) { } if (user == null) { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.callFailed", null, request.getLocale())); return returnResponse; } try { String refId = ((TelephoneVerificationService) connectorManagementService .getOssServiceInstancebycategory(ConnectorType.PHONE_VERIFICATION)).requestCall( user.getCountryCode(), user.getPhoneWithoutIsdCode(), generatedPhoneVerificationPin); if (refId != null) { returnResponse.put("result", "success"); returnResponse.put("message", messageSource.getMessage("js.errors.register.callRequested", null, request.getLocale())); } else { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.callFailed", null, request.getLocale())); } } catch (TelephoneVerificationServiceException e) { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.callFailed", null, request.getLocale())); } return returnResponse; } /** * @param userName * @param pickValFromReset * @param request * @return * @throws JsonGenerationException * @throws JsonMappingException * @throws IOException */ @RequestMapping(value = "/request_sms_by_user", method = RequestMethod.POST) @ResponseBody public Map<String, String> requestSMS(@RequestParam(value = "userName", required = false) final String userName, @RequestParam(value = "pickValFromReset", required = false) final String pickValFromReset, HttpServletRequest request) throws JsonGenerationException, JsonMappingException, IOException { String generatedPhoneVerificationPin = request.getSession().getAttribute("phoneVerificationPin").toString(); final String userNameLoc; Map<String, String> returnResponse = new HashMap<String, String>(); if (pickValFromReset != null && pickValFromReset.equals("pick")) { userNameLoc = (String) request.getSession().getAttribute(RESET_USER_KEY); } else if (userName != null) { userNameLoc = userName; } else { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.textMessageFailed", null, request.getLocale())); return returnResponse; } request.getSession().setAttribute("userName", userNameLoc); User user = null; try { user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.getUserByParam("username", userNameLoc, false); return user; } }); } catch (NoSuchUserException e) { } if (user == null) { returnResponse.put("result", "success"); // Returning success for security // reason. returnResponse.put("message", messageSource.getMessage("js.errors.register.textMessageRequested", null, request.getLocale())); return returnResponse; } try { String refId = ((TelephoneVerificationService) connectorManagementService .getOssServiceInstancebycategory(ConnectorType.PHONE_VERIFICATION)).requestSMS( user.getCountryCode(), user.getPhoneWithoutIsdCode(), generatedPhoneVerificationPin); if (refId != null) { returnResponse.put("result", "success"); returnResponse.put("message", messageSource.getMessage("js.errors.register.textMessageRequested", null, request.getLocale())); } else { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.textMessageFailed", null, request.getLocale())); } } catch (TelephoneVerificationServiceException e) { returnResponse.put("result", "failed"); returnResponse.put("message", messageSource.getMessage("js.errors.register.textMessageFailed", null, request.getLocale())); } return returnResponse; } @RequestMapping(value = "/phoneverification/verifyPINForUnlock", method = RequestMethod.GET) @ResponseBody public String verifyPhoneVerificationPINForUnlock(@RequestParam(value = "PIN", required = true) String PIN, HttpServletRequest request) { String generatedPhoneVerificationPin = (String) request.getSession().getAttribute("phoneVerificationPin"); if (PIN != null && PIN.equals(generatedPhoneVerificationPin)) { return "success"; } return "failed"; } @RequestMapping(value = "/reset_password", method = RequestMethod.POST, params = "username") public String requestReset(@RequestParam(value = "username", required = true) final String username, HttpServletRequest request, ModelMap map) { logger.debug("###Entering in requestReset(username,request) method @POST"); String showSuffixControl = "false"; if (config.getValue(Names.com_citrix_cpbm_username_duplicate_allowed).equals("true")) { showSuffixControl = "true"; map.addAttribute("useSmallCss", "true"); } map.addAttribute("showSuffixControl", showSuffixControl); try { User user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { return userService.getUserByParam("username", username, false); } }); if (user.isEmailVerified()) { userService.sendResetPasswordMail(user, "reset.password", user.getUsername()); } } catch (NoSuchUserException ex) { // no indication to user that this failed. return "auth.request_reset_success"; } logger.debug("###Exiting requestReset(username,request) method @POST"); return "auth.request_reset_success"; } @RequestMapping(value = "/reset_password", method = RequestMethod.GET, params = "a") public String reset(@RequestParam(value = "a", required = true) final String auth, @RequestParam(value = "t", required = true) final long ts, @RequestParam(value = "i", required = true) final String userParam, HttpSession session, ModelMap map) { logger.debug("###Entering in reset(auth,ts,userId,session) method @GET"); Random rnd = new Random(); int n = 99999 - 1000; session.setAttribute("phoneVerificationPin", "" + rnd.nextInt(n)); User user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.get(userParam); userService.verifyAuthorization(user, auth, ts); return user; } }); // Whether telesign is enabled and the user is master user? boolean isTelesignVerificationEnable = false; if ((TelephoneVerificationService) connectorManagementService .getOssServiceInstancebycategory(ConnectorType.PHONE_VERIFICATION) != null) { isTelesignVerificationEnable = ((TelephoneVerificationService) connectorManagementService .getOssServiceInstancebycategory(ConnectorType.PHONE_VERIFICATION)).isEnabled() && user.equals(user.getTenant().getOwner()); } map.addAttribute("isTelesignVerificationEnable", isTelesignVerificationEnable); session.setAttribute(RESET_USER_KEY, user.getUsername()); logger.debug("###Exiting in reset(auth,ts,userId,session) method @GET"); return "auth.reset"; } @RequestMapping(value = "/reset_password", method = RequestMethod.POST, params = "password") public String reset(@RequestParam(value = "password", required = true) final String password, HttpSession session) { logger.debug("###Entering in reset(password,session) method @POST"); final String username = (String) session.getAttribute(RESET_USER_KEY); final int maxFailCount = config .getIntValue(Names.com_citrix_cpbm_accountManagement_security_logins_lockThreshold); User user = privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.getUserByParam("username", username, false); if (!config.getBooleanValue(Configuration.Names.com_citrix_cpbm_portal_directory_service_enabled)) { user.setClearPassword(password); } else if (config.getValue(Names.com_citrix_cpbm_directory_mode).equals("push")) { userService.updateUserPassword(password, user.getUuid()); } if (!user.isEnabled() || user.getFailedLoginAttempts() >= maxFailCount) { user.setFailedLoginAttempts(0); user.setEnabled(true); } return user; } }); // Looks out of place, but useful. if (user.getFailedLoginAttempts() >= config .getIntValue(Names.com_citrix_cpbm_accountManagement_security_logins_captchaThreshold)) { session.setAttribute(CaptchaAuthenticationFilter.CAPTCHA_REQUIRED, true); } session.removeAttribute(RESET_USER_KEY); logger.debug("###Exiting in reset(password,session) method @POST"); return "redirect:/portal/login"; } @RequestMapping(value = "/verify_additional_email", method = RequestMethod.GET) public String verifyAdditionalEmail(@RequestParam(value = "a", required = true) final String auth, @RequestParam(value = "i", required = true) final String userParam, @RequestParam(value = "pi", required = true) final String cryptedEmail, final HttpServletRequest request, ModelMap map, HttpSession session) { logger.debug("###Entering in verifyAlertEmail(map) method @GET"); privilegeService.runAsPortal(new PrivilegedAction<User>() { @Override public User run() { User user = userService.get(userParam); userService.verifyAuthorization(user, auth, 0); String emailAdd = CryptoUtils.decrypt(cryptedEmail, CryptoUtils.keyGenerationSeed); UserAlertPreferences userAlertPreferences = userAlertPreferencesService .locateUserAlertPreference(user, emailAdd); if (userAlertPreferences.getAlertType() == AlertType.USER_EMAIL) { user.setEmail(userAlertPreferences.getEmailAddress()); userService.save(user); } else { userAlertPreferences.setEmailVerified(true); userAlertPreferencesService.save(userAlertPreferences); } return user; } }); map.clear(); // No need for map to propagate session.setAttribute("email_verified", "Y"); logger.debug("##Exiting verifyAlertEmail(map) method @GET"); return "redirect:/portal/home"; } @RequestMapping(value = "getGoogleAnalytics", method = RequestMethod.GET) @ResponseBody public Map<String, String> getGoogleAnalytics() { Map<String, String> map = new HashMap<String, String>(); map.put("enabled", config.getValue(Names.com_citrix_cpbm_portal_integrations_analytics)); map.put("account", config.getValue(Names.integration_analytics_google_analytics_account)); map.put("domain", config.getValue(Names.integration_analytics_google_analytics_domain)); return map; } }