begin tidy up of error handling for authentication.

This commit is contained in:
Keith Woodlock
2012-05-21 02:08:08 +01:00
parent 52de332e5e
commit 4019a783a7
9 changed files with 142 additions and 99 deletions
@@ -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.");
@@ -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<AuthenticatedUserData> 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<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
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<AuthenticatedUserData> s = this.restTemplate.postForEntity(restUri, authRequest(), AuthenticatedUserData.class);
UserDetails userDetails = null;
AuthenticatedUserData authenticatedUserData = s.getBody();
if (authenticatedUserData.isAuthenticated()) {
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
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<Object> authRequest() {
@@ -7,11 +7,13 @@
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<http security="none" pattern="/login" />
<http security="none" pattern="/logout" />
<http use-expressions="true" pattern="/**" create-session="always">
<intercept-url pattern="/**" access="isFullyAuthenticated()" />
<!-- login-page="/login" -->
<form-login />
<logout invalidate-session="true" logout-url="/signout" logout-success-url="/logout" />
</http>
@@ -50,8 +50,8 @@
<mvc:resources location="/WEB-INF/static/" mapping="/resources/**" cache-period="31556926"/>
<mvc:view-controller path="/login" view-name="login"/>
<mvc:view-controller path="/logout" view-name="logout"/>
<mvc:view-controller path="/oauth_error" view-name="oauth_error"/>
<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
@@ -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" %>
<c:set var="applicationName" scope="session" value="Mifos - Individual Lending: " />
<c:set var="pageTitle" scope="session" value="Login"/>
<!DOCTYPE html>
<html lang="en">
<head>
<jsp:include page="common-head.jsp" />
<script>
$(document).ready(function() {
$('#login').button();
});
</script>
</head>
<body>
<div id="container">
<div id="content">
<div id="spacer" style="line-height: 25px;">&nbsp;</div>
</div>
</div>
</body>
</html>
<!--
<h1>Mifos - Individual Lending: Login</h1>
<div id="formcontainer">
<c:if test="${!empty sessionScope.SPRING_SECURITY_LAST_EXCEPTION}">
<div class="ui-widget">
<div class="ui-state-error ui-corner-all">
<span class="ui-icon ui-icon-alert" style="float: left; margin-right: 5px;" ></span>
<span>You have the following errors:</span>
<div style="margin-left: 5px;">
<p>Your login attempt was not successful. (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)</p>
</div>
</div>
</div>
</c:if>
<c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/>
<c:url value="/j_spring_security_check" var="loginFilterUrl" />
<c:url value="${successUrl}" var="authSuccessUrl" />
<form method="post" action="${loginFilterUrl}">
<fieldset>
<legend>Login</legend>
<label for="username">Username:</label>
<input type="hidden" name="successUrl" value="${successUrl}" />
<c:choose>
<c:when test="${param.oauth_token == null}">
<input id="testone" type="hidden" name="oauth_token" value="${requestScope.oauth_token}" />
</c:when>
<c:otherwise>
<input id="testtwo" type="hidden" name="oauth_token" value="${param.oauth_token}" />
</c:otherwise>
</c:choose>
<input size="75" type="text" name="j_username" id="username" title="Username for authentication." />
<label for="password">Password:</label>
<input size="75" type="password" name="j_password" id="password" title="Password for authentication."/>
<div id="formbuttons">
<button type="submit" id="login" name="login" title="Log in to application.">Login</button>
</div>
</fieldset>
</form>
-->
@@ -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<BadCredentialsException> {
@Override
public Response toResponse(BadCredentialsException exception) {
return Response.status(Status.UNAUTHORIZED).entity(ApiGlobalErrorResponse.unAuthenticated()).build();
}
}
@@ -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");
}
}
@@ -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;
}
}
@@ -20,6 +20,13 @@
<div id="container">
<div id="content">
<div id="spacer" style="line-height: 25px;">&nbsp;</div>
</div>
</div>
</body>
</html>
<!--
<h1>Mifos - Individual Lending: Login</h1>
<div id="formcontainer">
@@ -63,8 +70,4 @@
</div>
</fieldset>
</form>
</div>
</div>
</div>
</body>
</html>
-->