/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.frontend.web.controllers; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.orcid.core.security.UnclaimedProfileExistsException; import org.orcid.core.security.aop.LockedException; import org.orcid.persistence.jpa.entities.ClientDetailsEntity; import org.orcid.pojo.ajaxForm.OauthAuthorizeForm; import org.orcid.pojo.ajaxForm.PojoUtil; import org.orcid.pojo.ajaxForm.RequestInfoForm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.DisabledException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.exceptions.InvalidScopeException; import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException; import org.springframework.security.web.savedrequest.HttpSessionRequestCache; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.support.SimpleSessionStatus; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; @Controller("oauthLoginController") public class OauthLoginController extends OauthControllerBase { private static final Logger LOGGER = LoggerFactory.getLogger(OauthLoginController.class); @Value("${org.orcid.frontend.oauthSignin.showLogin.default:true}") private boolean showLoginDefault; @RequestMapping(value = { "/oauth/signin", "/oauth/login" }, method = RequestMethod.GET) public ModelAndView loginGetHandler(HttpServletRequest request, HttpServletResponse response, ModelAndView mav) throws UnsupportedEncodingException { String url = request.getQueryString(); boolean showLogin = showLoginDefault; // default to Reg // Get and save the request information form RequestInfoForm requestInfoForm = generateRequestInfoForm(url); request.getSession().setAttribute(REQUEST_INFO_FORM, requestInfoForm); if (url.toLowerCase().contains("show_login=true")) showLogin = true; else if (url.toLowerCase().contains("show_login=false")) showLogin = false; //Check if userId is set so we should show the login screen if(!PojoUtil.isEmpty(requestInfoForm.getUserId())) { showLogin = true; } // Check that the client have the required permissions // Get client name ClientDetailsEntity clientDetails = clientDetailsEntityCacheManager.retrieve(requestInfoForm.getClientId()); // validate client scopes try { authorizationEndpoint.validateScope(requestInfoForm.getScopesAsString(), clientDetails); orcidOAuth2RequestValidator.validateClientIsEnabled(clientDetails); } catch (InvalidScopeException | LockedException e) { String redirectUriWithParams = requestInfoForm.getRedirectUrl(); if (e instanceof InvalidScopeException) { redirectUriWithParams += "?error=invalid_scope&error_description=" + e.getMessage(); } else { redirectUriWithParams += "?error=client_locked&error_description=" + e.getMessage(); } RedirectView rView = new RedirectView(redirectUriWithParams); ModelAndView error = new ModelAndView(); error.setView(rView); return error; } mav.addObject("hideUserVoiceScript", true); mav.addObject("showLogin", String.valueOf(showLogin)); mav.setViewName("oauth_login"); return mav; } @RequestMapping(value = { "/oauth/custom/signin.json", "/oauth/custom/login.json" }, method = RequestMethod.POST) public @ResponseBody OauthAuthorizeForm authenticateAndAuthorize(HttpServletRequest request, HttpServletResponse response, @RequestBody OauthAuthorizeForm form) { // Clean form errors form.setErrors(new ArrayList<String>()); RequestInfoForm requestInfoForm = (RequestInfoForm) request.getSession().getAttribute(REQUEST_INFO_FORM); boolean willBeRedirected = false; if (form.getApproved()) { // Validate name and password validateUserNameAndPassword(form); if (form.getErrors().isEmpty()) { try { // Authenticate user Authentication auth = authenticateUser(request, form); // Create authorization params SimpleSessionStatus status = new SimpleSessionStatus(); Map<String, Object> model = new HashMap<String, Object>(); Map<String, String> params = new HashMap<String, String>(); Map<String, String> approvalParams = new HashMap<String, String>(); fillOauthParams(requestInfoForm, params, approvalParams, form.getPersistentTokenEnabled(), form.isEmailAccessAllowed()); // Authorize try { authorizationEndpoint.authorize(model, params, status, auth); } catch (RedirectMismatchException rUriError) { String redirectUri = this.getBaseUri() + REDIRECT_URI_ERROR; // Set the client id redirectUri = redirectUri.replace("{0}", requestInfoForm.getClientId()); // Set the response type if needed if (!PojoUtil.isEmpty(requestInfoForm.getResponseType())) redirectUri += "&response_type=" + requestInfoForm.getResponseType(); // Set the redirect uri if (!PojoUtil.isEmpty(requestInfoForm.getRedirectUrl())) redirectUri += "&redirect_uri=" + requestInfoForm.getRedirectUrl(); // Set the scope param if (!PojoUtil.isEmpty(requestInfoForm.getScopesAsString())) redirectUri += "&scope=" + requestInfoForm.getScopesAsString(); // Copy the state param if present if (!PojoUtil.isEmpty(requestInfoForm.getStateParam())) redirectUri += "&state=" + requestInfoForm.getStateParam(); form.setRedirectUrl(redirectUri); LOGGER.info("OauthLoginController being sent to client browser: " + form.getRedirectUrl()); return form; } // Approve RedirectView view = (RedirectView) authorizationEndpoint.approveOrDeny(approvalParams, model, status, auth); form.setRedirectUrl(view.getUrl()); willBeRedirected = true; } catch (AuthenticationException ae) { if(ae.getCause() instanceof DisabledException){ // Handle this message in angular to allow AJAX action form.getErrors().add("orcid.frontend.security.orcid_deactivated"); } else if(ae.getCause() instanceof UnclaimedProfileExistsException) { String email = PojoUtil.isEmpty(form.getUserName()) ? null : form.getUserName().getValue(); String resendEmailUrl = createResendClaimUrl(email, request); String errorMessage = getMessage("orcid.frontend.security.unclaimed_exists"); errorMessage = errorMessage.replace("{{resendClaimUrl}}", resendEmailUrl); form.getErrors().add(errorMessage); } else { form.getErrors().add(getMessage("orcid.frontend.security.bad_credentials")); } } } } else { form.setRedirectUrl(buildDenyRedirectUri(requestInfoForm.getRedirectUrl(), requestInfoForm.getStateParam())); willBeRedirected = true; } // If there was an authentication error, dont log since the user will // not be redirected yet if (willBeRedirected) { if (new HttpSessionRequestCache().getRequest(request, response) != null) new HttpSessionRequestCache().removeRequest(request, response); LOGGER.info("OauthConfirmAccessController form.getRedirectUri being sent to client browser: " + requestInfoForm.getRedirectUrl()); } return form; } private void validateUserNameAndPassword(OauthAuthorizeForm form) { if (PojoUtil.isEmpty(form.getUserName()) || PojoUtil.isEmpty(form.getPassword())) { form.getErrors().add(getMessage("orcid.frontend.security.bad_credentials")); } } }