All Downloads are FREE. Search and download functionalities are using the official Maven repository.

clime.messadmin.admin.HTTPAuthorizationProvider Maven / Gradle / Ivy

Go to download

Notification system and Session administration for J2EE Web Applications

There is a newer version: 4.1.1
Show newest version
package clime.messadmin.admin;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * NOTE: the Base64 implementation come from Jakarta commons Codec 1.3.
 * 
 * @author Cédrik LIME
 */
class HTTPAuthorizationProvider {

	/**
	 * Byte used to pad output of Base64 data.
	 */
	private static final byte PAD = (byte) '=';

	// Create arrays to hold the base64 characters and a lookup for base64 chars
	private static byte[] base64Alphabet = new byte[255];
//	private static byte[] lookUpBase64Alphabet = new byte[64];

	// Populating the lookup and character arrays
	static {
		Arrays.fill(base64Alphabet, (byte)-1);
		for (int i = 'Z'; i >= 'A'; --i) {
			base64Alphabet[i] = (byte) (i - 'A');
		}
		for (int i = 'z'; i >= 'a'; --i) {
			base64Alphabet[i] = (byte) (i - 'a' + 26);
		}
		for (int i = '9'; i >= '0'; --i) {
			base64Alphabet[i] = (byte) (i - '0' + 52);
		}
		base64Alphabet['+'] = 62;
		base64Alphabet['/'] = 63;

//		for (int i = 0; i <= 25; ++i) {
//			lookUpBase64Alphabet[i] = (byte) ('A' + i);
//		}
//		for (int i = 26, j = 0; i <= 51; ++i, ++j) {
//			lookUpBase64Alphabet[i] = (byte) ('a' + j);
//		}
//		for (int i = 52, j = 0; i <= 61; ++i, ++j) {
//			lookUpBase64Alphabet[i] = (byte) ('0' + j);
//		}
//		lookUpBase64Alphabet[62] = (byte) '+';
//		lookUpBase64Alphabet[63] = (byte) '/';
	}

	/**
	 * Encodes binary data using the base64 algorithm but
	 * does not chunk the output.
	 *
	 * @param binaryData Array containing binary data to encode.
	 * @return Base64-encoded data.
	 */
//	private static byte[] encodeBase64(byte[] binaryData) {
//		/** Used to test the sign of a byte. */
//		final int SIGN = -128;
//
//		int lengthDataBits = binaryData.length * 8;//EIGHTBIT
//		int fewerThan24bits = lengthDataBits % 24;//TWENTYFOURBITGROUP
//		int numberTriplets = lengthDataBits / 24;//TWENTYFOURBITGROUP
//		byte encodedData[] = null;
//		int encodedDataLength = 0;
//
//		if (fewerThan24bits != 0) {
//			//data not divisible by 24 bit
//			encodedDataLength = (numberTriplets + 1) * 4;
//		} else {
//			// 16 or 8 bit
//			encodedDataLength = numberTriplets * 4;
//		}
//
//		encodedData = new byte[encodedDataLength];
//
//		byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
//
//		int encodedIndex = 0;
//		int dataIndex = 0;
//		int i = 0;
//
//		//log.debug("number of triplets = " + numberTriplets);
//		for (i = 0; i < numberTriplets; ++i) {
//			dataIndex = i * 3;
//			b1 = binaryData[dataIndex];
//			b2 = binaryData[dataIndex + 1];
//			b3 = binaryData[dataIndex + 2];
//
//			//log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
//
//			l = (byte) (b2 & 0x0f);
//			k = (byte) (b1 & 0x03);
//
//			byte val1 =
//				((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
//			byte val2 =
//				((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
//			byte val3 =
//				((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
//
//			encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//			//log.debug( "val2 = " + val2 );
//			//log.debug( "k4   = " + (k<<4) );
//			//log.debug( "vak  = " + (val2 | (k<<4)) );
//			encodedData[encodedIndex + 1] =
//				lookUpBase64Alphabet[val2 | (k << 4)];
//			encodedData[encodedIndex + 2] =
//				lookUpBase64Alphabet[(l << 2) | val3];
//			encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
//
//			encodedIndex += 4;
//		}
//
//		// form integral number of 6-bit groups
//		dataIndex = i * 3;
//
//		if (fewerThan24bits == 8) {//EIGHTBIT
//			b1 = binaryData[dataIndex];
//			k = (byte) (b1 & 0x03);
//			//log.debug("b1=" + b1);
//			//log.debug("b1<<2 = " + (b1>>2) );
//			byte val1 =
//				((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
//			encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//			encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
//			encodedData[encodedIndex + 2] = PAD;
//			encodedData[encodedIndex + 3] = PAD;
//		} else if (fewerThan24bits == 16) {//SIXTEENBIT
//
//			b1 = binaryData[dataIndex];
//			b2 = binaryData[dataIndex + 1];
//			l = (byte) (b2 & 0x0f);
//			k = (byte) (b1 & 0x03);
//
//			byte val1 =
//				((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
//			byte val2 =
//				((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
//
//			encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
//			encodedData[encodedIndex + 1] =
//				lookUpBase64Alphabet[val2 | (k << 4)];
//			encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
//			encodedData[encodedIndex + 3] = PAD;
//		}
//
//		return encodedData;
//	}

	/**
	 * Decodes Base64 data into octects
	 *
	 * @param base64Data Byte array containing Base64 data
	 * @return Array containing decoded data.
	 */
	private static byte[] decodeBase64(byte[] base64Data) {
		// RFC 2045 requires that we discard ALL non-Base64 characters
		base64Data = discardNonBase64(base64Data);

		// handle the edge case, so we don't have to worry about it later
		if (base64Data.length == 0) {
			return new byte[0];
		}

		int numberQuadruple = base64Data.length / 4;//FOURBYTE
		byte decodedData[] = null;
		byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;

		// Throw away anything not in base64Data

		int encodedIndex = 0;
		int dataIndex = 0;
		{
			// this sizes the output array properly - rlw
			int lastData = base64Data.length;
			// ignore the '=' padding
			while (base64Data[lastData - 1] == PAD) {
				if (--lastData == 0) {
					return new byte[0];
				}
			}
			decodedData = new byte[lastData - numberQuadruple];
		}
		
		for (int i = 0; i < numberQuadruple; ++i) {
			dataIndex = i * 4;
			marker0 = base64Data[dataIndex + 2];
			marker1 = base64Data[dataIndex + 3];
			
			b1 = base64Alphabet[base64Data[dataIndex]];
			b2 = base64Alphabet[base64Data[dataIndex + 1]];
			
			if (marker0 != PAD && marker1 != PAD) {
				//No PAD e.g 3cQl
				b3 = base64Alphabet[marker0];
				b4 = base64Alphabet[marker1];
				
				decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
				decodedData[encodedIndex + 1] =
					(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
				decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
			} else if (marker0 == PAD) {
				//Two PAD e.g. 3c[Pad][Pad]
				decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
			} else if (marker1 == PAD) {
				//One PAD e.g. 3cQ[Pad]
				b3 = base64Alphabet[marker0];
				
				decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
				decodedData[encodedIndex + 1] =
					(byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
			}
			encodedIndex += 3;
		}
		return decodedData;
	}
	
	/**
	 * Discards any characters outside of the base64 alphabet, per
	 * the requirements on page 25 of RFC 2045 - "Any characters
	 * outside of the base64 alphabet are to be ignored in base64
	 * encoded data."
	 *
	 * @param data The base-64 encoded data to groom
	 * @return The data, less non-base64 characters (see RFC 2045).
	 */
	private static byte[] discardNonBase64(byte[] data) {
		byte groomedData[] = new byte[data.length];
		int bytesCopied = 0;

		for (int i = 0; i < data.length; ++i) {
			if (isBase64(data[i])) {
				groomedData[bytesCopied++] = data[i];
			}
		}

		byte packedData[] = new byte[bytesCopied];
		System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
		return packedData;
	}

	private static final boolean isBase64(byte octect) {
		if (octect == PAD) {
			return true;
		} else {
			return base64Alphabet[octect] != -1;
		}
	}

	private static String md5(String message) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");//$NON-NLS-1$
			byte[] md5 = md.digest(message.getBytes());
			return new String(md5);
		} catch (NoSuchAlgorithmException nsae) {
			throw new RuntimeException("Error while computing md5 hash: " + nsae.getLocalizedMessage());
		}
	}


	public static boolean checkAccess(final String authorizationPassword, HttpServletRequest request, HttpServletResponse response) throws IOException {
		// no password set => no authorization required
		if (authorizationPassword == null || "".equals(authorizationPassword.trim())) {
			return true;
		}

		// password from a previously-set authorization cookie?
		if (request.getCookies() != null) {
			Cookie[] cookies = request.getCookies();
			String authorizationPasswordHash = md5(authorizationPassword);
			// some browsers always set null for cookie.getPath()...
			String cookieName = "MessAdmin" + request.getContextPath();//$NON-NLS-1$
			for (int i = 0; i < cookies.length; ++i) {
				Cookie cookie = cookies[i];
				if (cookieName.equals(cookie.getName())) {
					String providedPasswordHash = cookie.getValue();
					if (authorizationPasswordHash.equals(providedPasswordHash)) {
						return true;
					}
				}
			}
		}

		// we must authenticate the user before letting her play with us
		String providedPassword = null;
		if (request.getParameter("password") != null) {//$NON-NLS-1$
			// password in URL
			providedPassword = request.getParameter("password");//$NON-NLS-1$
			//request.setAttribute("password", providedPassword);//$NON-NLS-1$
		} else if (request.getHeader("Authorization") != null) {//$NON-NLS-1$
			// password from HTTP Access Authentication
			String authorization = request.getHeader("Authorization");//$NON-NLS-1$
			if (! authorization.startsWith("Basic ")) {//$NON-NLS-1$
				//TODO we should use "Digest" instead of "Basic", but it is more complicated to code...
				throw new IllegalArgumentException("Only Basic HTTP Access Authentication supported");
			}
			String base64UserPass = authorization.substring("Basic ".length()).trim();//$NON-NLS-1$
			String userPass = new String(decodeBase64(base64UserPass.getBytes()));
			int index = userPass.indexOf(':');
			//String user = userPass.substring(0, index);
			String password = userPass.substring(index+1);
			providedPassword = password;
		} else {
			providedPassword = null;
		}

		if (authorizationPassword.equals(providedPassword)) {
			// set authorization Cookie
			// some browsers always set null for cookie.getPath()...
			String cookieName = "MessAdmin" + request.getContextPath();//$NON-NLS-1$
			Cookie cookie = new Cookie(cookieName, md5(authorizationPassword));
			cookie.setVersion(1);
			cookie.setMaxAge(-1);
			cookie.setPath(request.getContextPath());
			response.addCookie(cookie);
			return true;
		} else {
			// request Authorization Password
			//TODO we should use "Digest" instead of "Basic", but it is more complicated to code...
			response.setHeader("WWW-Authenticate", "Basic realm=\"MessAdmin Administration for " + request.getContextPath() + '"');//$NON-NLS-1$ //$NON-NLS-2$
			response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy