From 4019a783a77b73dc6566d78f904b288aec070679 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Mon, 21 May 2012 02:08:08 +0100 Subject: [PATCH] begin tidy up of error handling for authentication. --- .../mifosng/data/ApiGlobalErrorResponse.java | 2 +- .../ApiDelegatingAuthenticationProvider.java | 49 ++++++++----- .../META-INF/spring/securityContext.xml | 2 + .../webapp/WEB-INF/spring/webmvc-config.xml | 2 +- .../src/main/webapp/WEB-INF/views/login.jsp | 73 +++++++++++++++++++ .../BadCredentialsExceptionMapper.java | 27 +++++++ .../ApiAuthenticationEntryPoint.java | 21 ------ .../ApiAuthenticationSuccessHandler.java | 52 ------------- .../src/main/webapp/WEB-INF/views/login.jsp | 13 ++-- 9 files changed, 142 insertions(+), 99 deletions(-) create mode 100644 mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/login.jsp create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/BadCredentialsExceptionMapper.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationEntryPoint.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationSuccessHandler.java diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java index 7a093a53b..acf7e9e46 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java @@ -47,7 +47,7 @@ public class ApiGlobalErrorResponse { ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); globalErrorResponse.setHttpStatusCode("401"); - globalErrorResponse.setDeveloperMessage("No authentication details were passed in api request."); + globalErrorResponse.setDeveloperMessage("Invalid authentication details were passed in api request."); globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated"); globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login."); diff --git a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/infrastructure/ApiDelegatingAuthenticationProvider.java b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/infrastructure/ApiDelegatingAuthenticationProvider.java index 0428f26a1..82bab0f7b 100644 --- a/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/infrastructure/ApiDelegatingAuthenticationProvider.java +++ b/mifosng-individual-lending-app/src/main/java/org/mifosng/ui/infrastructure/ApiDelegatingAuthenticationProvider.java @@ -9,7 +9,9 @@ import org.mifosng.data.AuthenticatedUserData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; @@ -17,6 +19,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; /** @@ -43,28 +46,36 @@ public class ApiDelegatingAuthenticationProvider extends AbstractUserDetailsAuth @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { - String password = (String) authentication.getCredentials(); - - String platformApiUrl = this.applicationConfigurationService.retrievePlatformApiUrl(); - String authUrlExtension = "authentication?username=" + username + "&password=" + password; - StringBuilder authenticationApiUrl = new StringBuilder(platformApiUrl).append(authUrlExtension); - - URI restUri = URI.create(authenticationApiUrl.toString()); - ResponseEntity s = this.restTemplate.postForEntity(restUri, authRequest(), AuthenticatedUserData.class); - - UserDetails userDetails = null; - AuthenticatedUserData authenticatedUserData = s.getBody(); - if (authenticatedUserData.isAuthenticated()) { + try { + String password = (String) authentication.getCredentials(); - Collection authorities = new ArrayList(); - for (String permission : authenticatedUserData.getPermissions()) { - authorities.add(new SimpleGrantedAuthority(permission)); + String platformApiUrl = this.applicationConfigurationService.retrievePlatformApiUrl(); + String authUrlExtension = "authentication?username=" + username + "&password=" + password; + StringBuilder authenticationApiUrl = new StringBuilder(platformApiUrl).append(authUrlExtension); + + URI restUri = URI.create(authenticationApiUrl.toString()); + ResponseEntity s = this.restTemplate.postForEntity(restUri, authRequest(), AuthenticatedUserData.class); + + UserDetails userDetails = null; + AuthenticatedUserData authenticatedUserData = s.getBody(); + if (authenticatedUserData.isAuthenticated()) { + + Collection authorities = new ArrayList(); + for (String permission : authenticatedUserData.getPermissions()) { + authorities.add(new SimpleGrantedAuthority(permission)); + } + User user = new User(authenticatedUserData.getUsername(), "[not needed]", authorities); + userDetails = new BasicAuthUserDetails(user, authenticatedUserData.getBase64EncodedAuthenticationKey(), platformApiUrl); } - User user = new User(authenticatedUserData.getUsername(), "[not needed]", authorities); - userDetails = new BasicAuthUserDetails(user, authenticatedUserData.getBase64EncodedAuthenticationKey(), platformApiUrl); + + return userDetails; + } catch (HttpClientErrorException clientErrorException) { + if (clientErrorException.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { + throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + + throw clientErrorException; } - - return userDetails; } private HttpEntity authRequest() { diff --git a/mifosng-individual-lending-app/src/main/resources/META-INF/spring/securityContext.xml b/mifosng-individual-lending-app/src/main/resources/META-INF/spring/securityContext.xml index ab4fe46f5..83d40a7ec 100644 --- a/mifosng-individual-lending-app/src/main/resources/META-INF/spring/securityContext.xml +++ b/mifosng-individual-lending-app/src/main/resources/META-INF/spring/securityContext.xml @@ -7,11 +7,13 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> + + diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/spring/webmvc-config.xml b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/spring/webmvc-config.xml index fb30fa772..b3aa80824 100644 --- a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/spring/webmvc-config.xml +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/spring/webmvc-config.xml @@ -50,8 +50,8 @@ + - diff --git a/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/login.jsp b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/login.jsp new file mode 100644 index 000000000..f123cacf4 --- /dev/null +++ b/mifosng-individual-lending-app/src/main/webapp/WEB-INF/views/login.jsp @@ -0,0 +1,73 @@ +<%@ page session="true" %> +<%@ page import="org.springframework.security.core.AuthenticationException" %> +<%@ page import="org.springframework.security.web.WebAttributes" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> + + + + + + + + + + +
+
+
 
+
+
+ + + + + \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/BadCredentialsExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/BadCredentialsExceptionMapper.java new file mode 100644 index 000000000..32ca44277 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/BadCredentialsExceptionMapper.java @@ -0,0 +1,27 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.springframework.context.annotation.Scope; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link BadCredentialsException} thrown by platform during authentication into a HTTP API friendly format. + * + * The {@link BadCredentialsException} is thrown by spring security on platform when an attempt is made to authenticate using invalid username/password credentials. + */ +@Provider +@Component +@Scope("singleton") +public class BadCredentialsExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(BadCredentialsException exception) { + return Response.status(Status.UNAUTHORIZED).entity(ApiGlobalErrorResponse.unAuthenticated()).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationEntryPoint.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationEntryPoint.java deleted file mode 100644 index ebe6bfcf7..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationEntryPoint.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.mifosng.platform.api.infrastructure; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -@Component("apiAuthenticationEntryPoint") -public final class ApiAuthenticationEntryPoint implements AuthenticationEntryPoint { - - @Override - public final void commence(HttpServletRequest request, - HttpServletResponse response, AuthenticationException authException) - throws IOException { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthenticated"); - } -} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationSuccessHandler.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationSuccessHandler.java deleted file mode 100644 index 467217f41..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiAuthenticationSuccessHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.mifosng.platform.api.infrastructure; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; -import org.springframework.security.web.savedrequest.HttpSessionRequestCache; -import org.springframework.security.web.savedrequest.RequestCache; -import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.util.StringUtils; - -public class ApiAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - - private RequestCache requestCache = new HttpSessionRequestCache(); - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, - HttpServletResponse response, Authentication authentication) - throws ServletException, IOException { - SavedRequest savedRequest = requestCache.getRequest(request, response); - - if (savedRequest == null) { - super.onAuthenticationSuccess(request, response, authentication); - - return; - } - String targetUrlParameter = getTargetUrlParameter(); - if (isAlwaysUseDefaultTargetUrl() - || (targetUrlParameter != null && StringUtils.hasText(request - .getParameter(targetUrlParameter)))) { - requestCache.removeRequest(request, response); - super.onAuthenticationSuccess(request, response, authentication); - - return; - } - - clearAuthenticationAttributes(request); - -// // Use the DefaultSavedRequest URL -// String targetUrl = savedRequest.getRedirectUrl(); -// logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl); -// getRedirectStrategy().sendRedirect(request, response, targetUrl); - } - - public void setRequestCache(RequestCache requestCache) { - this.requestCache = requestCache; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/webapp/WEB-INF/views/login.jsp b/mifosng-provider/src/main/webapp/WEB-INF/views/login.jsp index c00e85f9f..f123cacf4 100644 --- a/mifosng-provider/src/main/webapp/WEB-INF/views/login.jsp +++ b/mifosng-provider/src/main/webapp/WEB-INF/views/login.jsp @@ -20,6 +20,13 @@
 
+
+
+ + + + + \ No newline at end of file