/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.auth;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Node;
import winstone.AuthenticationPrincipal;
import winstone.AuthenticationRealm;
import winstone.Logger;
import winstone.WinstoneRequest;
/**
* Handles HTTP basic authentication.
*
* @author mailto: <a href="rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: BasicAuthenticationHandler.java,v 1.5 2007/04/11 13:14:26 rickknowles Exp $
*/
public class BasicAuthenticationHandler extends BaseAuthenticationHandler {
public BasicAuthenticationHandler(Node loginConfigNode,
List constraintNodes, Set rolesAllowed,
AuthenticationRealm realm) {
super(loginConfigNode, constraintNodes, rolesAllowed, realm);
Logger.log(Logger.DEBUG, AUTH_RESOURCES,
"BasicAuthenticationHandler.Initialised", realmName);
}
/**
* Call this once we know that we need to authenticate
*/
protected void requestAuthentication(HttpServletRequest request,
HttpServletResponse response, String pathRequested)
throws IOException {
// Return unauthorized, and set the realm name
response.setHeader("WWW-Authenticate", "Basic Realm=\""
+ this.realmName + "\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, AUTH_RESOURCES
.getString("BasicAuthenticationHandler.UnauthorizedMessage"));
}
/**
* Handling the (possible) response
*/
protected boolean validatePossibleAuthenticationResponse(
HttpServletRequest request, HttpServletResponse response,
String pathRequested) throws IOException {
String authorization = request.getHeader("Authorization");
if ((authorization != null)
&& authorization.toLowerCase().startsWith("basic")) {
char[] inBytes = authorization.substring(5).trim().toCharArray();
byte[] outBytes = new byte[(int) (inBytes.length * 0.75f)]; // always mod 4 = 0
int length = decodeBase64(inBytes, outBytes, 0, inBytes.length, 0);
String decoded = new String(outBytes, 0, length);
int delimPos = decoded.indexOf(':');
if (delimPos != -1) {
AuthenticationPrincipal principal = this.realm
.authenticateByUsernamePassword(decoded.substring(0,
delimPos).trim(), decoded.substring(
delimPos + 1).trim());
if (principal != null) {
principal.setAuthType(HttpServletRequest.BASIC_AUTH);
if (request instanceof WinstoneRequest)
((WinstoneRequest) request).setRemoteUser(principal);
else if (request instanceof HttpServletRequestWrapper) {
HttpServletRequestWrapper wrapper = (HttpServletRequestWrapper) request;
if (wrapper.getRequest() instanceof WinstoneRequest)
((WinstoneRequest) wrapper.getRequest())
.setRemoteUser(principal);
else
Logger.log(Logger.WARNING, AUTH_RESOURCES,
"BasicAuthenticationHandler.CantSetUser",
wrapper.getRequest().getClass().getName());
} else
Logger.log(Logger.WARNING, AUTH_RESOURCES,
"BasicAuthenticationHandler.CantSetUser",
request.getClass().getName());
}
}
}
return true;
}
/**
* Decodes a byte array from base64
*/
public static int decodeBase64(char[] input, byte[] output,
int inOffset, int inLength, int outOffset) {
if (inLength == 0) {
return 0;
}
int outIndex = outOffset;
for (int inIndex = inOffset; inIndex < inLength; ) {
// Decode four bytes
int thisPassInBytes = Math.min(inLength - inIndex, 4);
while ((thisPassInBytes > 1) &&
(input[inIndex + thisPassInBytes - 1] == '=')) {
thisPassInBytes--;
}
if (thisPassInBytes == 2) {
int outBuffer = ((B64_DECODE_ARRAY[input[inIndex]] & 0xFF) << 18)
| ((B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF) << 12);
output[outIndex] = (byte) ((outBuffer >> 16) & 0xFF);
outIndex += 1;
} else if (thisPassInBytes == 3) {
int outBuffer = ((B64_DECODE_ARRAY[input[inIndex]] & 0xFF) << 18)
| ((B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF) << 12)
| ((B64_DECODE_ARRAY[input[inIndex + 2]] & 0xFF) << 6);
output[outIndex] = (byte) ((outBuffer >> 16) & 0xFF);
output[outIndex + 1] = (byte) ((outBuffer >> 8) & 0xFF);
outIndex += 2;
} else if (thisPassInBytes == 4) {
int outBuffer = ((B64_DECODE_ARRAY[input[inIndex]] & 0xFF) << 18)
| ((B64_DECODE_ARRAY[input[inIndex + 1]] & 0xFF) << 12)
| ((B64_DECODE_ARRAY[input[inIndex + 2]] & 0xFF) << 6)
| (B64_DECODE_ARRAY[input[inIndex + 3]] & 0xFF);
output[outIndex] = (byte) ((outBuffer >> 16) & 0xFF);
output[outIndex + 1] = (byte) ((outBuffer >> 8) & 0xFF);
output[outIndex + 2] = (byte) (outBuffer & 0xFF);
outIndex += 3;
}
inIndex += thisPassInBytes;
}
return outIndex;
}
private static byte B64_DECODE_ARRAY[] = new byte[] { -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, 62, // Plus sign
-1, -1, -1, 63, // Slash
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers
-1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Large letters
-1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Small letters
-1, -1, -1, -1 };
}