begin tidy up of error handling for authentication.
This commit is contained in:
@@ -47,7 +47,7 @@ public class ApiGlobalErrorResponse {
|
|||||||
|
|
||||||
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
|
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
|
||||||
globalErrorResponse.setHttpStatusCode("401");
|
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.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
|
||||||
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated");
|
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated");
|
||||||
globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login.");
|
globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login.");
|
||||||
|
|||||||
+27
-16
@@ -9,7 +9,9 @@ import org.mifosng.data.AuthenticatedUserData;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
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.context.SecurityContext;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,28 +46,36 @@ public class ApiDelegatingAuthenticationProvider extends AbstractUserDetailsAuth
|
|||||||
@Override
|
@Override
|
||||||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||||
|
|
||||||
String password = (String) authentication.getCredentials();
|
try {
|
||||||
|
String password = (String) authentication.getCredentials();
|
||||||
|
|
||||||
String platformApiUrl = this.applicationConfigurationService.retrievePlatformApiUrl();
|
String platformApiUrl = this.applicationConfigurationService.retrievePlatformApiUrl();
|
||||||
String authUrlExtension = "authentication?username=" + username + "&password=" + password;
|
String authUrlExtension = "authentication?username=" + username + "&password=" + password;
|
||||||
StringBuilder authenticationApiUrl = new StringBuilder(platformApiUrl).append(authUrlExtension);
|
StringBuilder authenticationApiUrl = new StringBuilder(platformApiUrl).append(authUrlExtension);
|
||||||
|
|
||||||
URI restUri = URI.create(authenticationApiUrl.toString());
|
URI restUri = URI.create(authenticationApiUrl.toString());
|
||||||
ResponseEntity<AuthenticatedUserData> s = this.restTemplate.postForEntity(restUri, authRequest(), AuthenticatedUserData.class);
|
ResponseEntity<AuthenticatedUserData> s = this.restTemplate.postForEntity(restUri, authRequest(), AuthenticatedUserData.class);
|
||||||
|
|
||||||
UserDetails userDetails = null;
|
UserDetails userDetails = null;
|
||||||
AuthenticatedUserData authenticatedUserData = s.getBody();
|
AuthenticatedUserData authenticatedUserData = s.getBody();
|
||||||
if (authenticatedUserData.isAuthenticated()) {
|
if (authenticatedUserData.isAuthenticated()) {
|
||||||
|
|
||||||
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
|
Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
|
||||||
for (String permission : authenticatedUserData.getPermissions()) {
|
for (String permission : authenticatedUserData.getPermissions()) {
|
||||||
authorities.add(new SimpleGrantedAuthority(permission));
|
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;
|
return userDetails;
|
||||||
|
} catch (HttpClientErrorException clientErrorException) {
|
||||||
|
if (clientErrorException.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) {
|
||||||
|
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw clientErrorException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpEntity<Object> authRequest() {
|
private HttpEntity<Object> authRequest() {
|
||||||
|
|||||||
@@ -7,11 +7,13 @@
|
|||||||
http://www.springframework.org/schema/beans
|
http://www.springframework.org/schema/beans
|
||||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
||||||
|
|
||||||
|
<http security="none" pattern="/login" />
|
||||||
<http security="none" pattern="/logout" />
|
<http security="none" pattern="/logout" />
|
||||||
|
|
||||||
<http use-expressions="true" pattern="/**" create-session="always">
|
<http use-expressions="true" pattern="/**" create-session="always">
|
||||||
<intercept-url pattern="/**" access="isFullyAuthenticated()" />
|
<intercept-url pattern="/**" access="isFullyAuthenticated()" />
|
||||||
|
|
||||||
|
<!-- login-page="/login" -->
|
||||||
<form-login />
|
<form-login />
|
||||||
<logout invalidate-session="true" logout-url="/signout" logout-success-url="/logout" />
|
<logout invalidate-session="true" logout-url="/signout" logout-success-url="/logout" />
|
||||||
</http>
|
</http>
|
||||||
|
|||||||
@@ -50,8 +50,8 @@
|
|||||||
|
|
||||||
<mvc:resources location="/WEB-INF/static/" mapping="/resources/**" cache-period="31556926"/>
|
<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="/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 -->
|
<!-- Resolves view names to protected .jsp resources within the /WEB-INF/views directory -->
|
||||||
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
<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;"> </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>
|
||||||
|
-->
|
||||||
+27
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
-21
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-52
@@ -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="container">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="spacer" style="line-height: 25px;"> </div>
|
<div id="spacer" style="line-height: 25px;"> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
<h1>Mifos - Individual Lending: Login</h1>
|
<h1>Mifos - Individual Lending: Login</h1>
|
||||||
<div id="formcontainer">
|
<div id="formcontainer">
|
||||||
|
|
||||||
@@ -63,8 +70,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
-->
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Reference in New Issue
Block a user