package com.bradmcevoy.http; import com.bradmcevoy.common.StringSplitUtils; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Holds authentication information for a request * * There are two sets of information: * - that which is present in the request * - that which is determined as part of performing authentication * * Note that even if authentication fails, this object will still be available * in the request - DO NOT USE THE PRESENCE OF THIS OBJECT TO CHECK FOR A VALID LOGIN!!! * * Instead use the tag property. This will ONLY be not null after a successful * authentication * * @author brad */ public class Auth { private static final Logger log = LoggerFactory.getLogger( Auth.class ); /** * Holds application specific user data, as returned from the authenticate * method on Resource * * This should be used to test for a valid login. */ private Object tag; public enum Scheme { BASIC, DIGEST }; private Scheme scheme; private String user; private String password; private String realm; private String nonce; private String uri; private String responseDigest; private String qop; private String nc; private String cnonce; private boolean nonceStale; // set by digest auth handler public Auth( String sAuth ) { // log.debug( "parse: " + sAuth); int pos = sAuth.indexOf( " " ); String schemeCode; String enc; if( pos >= 0 ) { schemeCode = sAuth.substring( 0, pos ); scheme = Scheme.valueOf( schemeCode.toUpperCase() ); enc = sAuth.substring( pos + 1 ); } else { // assume basic scheme = Scheme.BASIC; enc = sAuth; } if( scheme.equals( Scheme.BASIC ) ) { parseBasic( enc ); } else if( scheme.equals( Scheme.DIGEST ) ) { parseDigest( enc ); } } public Auth( String user, Object userTag ) { this.scheme = Scheme.BASIC; this.user = user; this.password = null; this.tag = userTag; } /** * * @return - the user property in the request. This MIGHT NOT be an * actual user */ public String getUser() { return user; } /** * Set after a successful authenticate method with a not-null value * * The actual value will be application dependent */ public void setTag( Object authTag ) { tag = authTag; } /** * Holds application specific user data, as returned from the authenticate * method on Resource * * This should be used to test for a valid login. */ public Object getTag() { return tag; } public String getPassword() { return password; } public Scheme getScheme() { return scheme; } public String getCnonce() { return cnonce; } public String getNc() { return nc; } public String getNonce() { return nonce; } public String getQop() { return qop; } public String getRealm() { return realm; } public String getResponseDigest() { return responseDigest; } public String getUri() { return uri; } public boolean isNonceStale() { return nonceStale; } /** * set by digest auth processing. Used to add stale nonce flag to challenge * * @param nonceStale */ public void setNonceStale( boolean nonceStale ) { this.nonceStale = nonceStale; } private void parseBasic( String enc ) { byte[] bytes = Base64.decodeBase64( enc.getBytes() ); String s = new String( bytes ); int pos = s.indexOf( ":" ); if( pos >= 0 ) { user = s.substring( 0, pos ); password = s.substring( pos + 1 ); } else { user = s; password = null; } } private void parseDigest( String s ) { String[] headerEntries = StringSplitUtils.splitIgnoringQuotes( s, ',' ); Map headerMap = StringSplitUtils.splitEachArrayElementAndCreateMap( headerEntries, "=", "\"" ); // log.debug( "headerMap: " + headerMap); user = (String) headerMap.get( "username" ); realm = (String) headerMap.get( "realm" ); nonce = (String) headerMap.get( "nonce" ); uri = (String) headerMap.get( "uri" ); responseDigest = (String) headerMap.get( "response" ); qop = (String) headerMap.get( "qop" ); // RFC 2617 extension nc = (String) headerMap.get( "nc" ); // RFC 2617 extension cnonce = (String) headerMap.get( "cnonce" ); // RFC 2617 extension } }