Index: pom.xml =================================================================== --- pom.xml (revision 97895) +++ pom.xml (working copy) @@ -48,8 +48,18 @@ jboss.web servlet-api + + + junit + junit + test + + + org.easymock + easymock + 2.4 + test - Index: src/main/java/org/jboss/security/negotiation/AuthorizationHeader.java =================================================================== --- src/main/java/org/jboss/security/negotiation/AuthorizationHeader.java (revision 0) +++ src/main/java/org/jboss/security/negotiation/AuthorizationHeader.java (revision 0) @@ -0,0 +1,129 @@ +/* + * Created on Dec 14, 2009 + */ +package org.jboss.security.negotiation; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.catalina.connector.Request; +import org.apache.log4j.Logger; +import org.jboss.util.Base64; + +/** + * Parses the Authorization header provided in the request and provides details + * about the header for determining how to authenticate the user, if possible. + * + * @author jacob@solutionsfit.com + * @version 1.0 + */ +public class AuthorizationHeader +{ + private static final Logger log = Logger.getLogger(AuthorizationHeader.class); + + private static final Pattern AUTH_SCHEME_PATTERN = Pattern.compile("(\\w+)(.*)"); + + private static final String AUTH_HEADER_KEY = "Authorization"; + private static final transient byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + private boolean emptyAuthHeader = true; + private String authScheme; + private String authToken; + + /** + * Creates an AuthorizationHeader instance from the provided request based + * on the Authorization header. + * + * @param request + * @return an AuthorizationHeader instance for the request + * @throws IOException + */ + public static AuthorizationHeader createFromRequest(final Request request) throws IOException + { + String authHeader = request.getHeader(AUTH_HEADER_KEY); + + log.debug("Header - " + authHeader); + + return new AuthorizationHeader(authHeader); + } + + protected AuthorizationHeader(final String authHeader) throws IOException + { + if(authHeader != null && authHeader.length() > 0) + { + boolean invalidHeader = false; + + authScheme = determineAuthScheme(authHeader); + + if(authScheme == null) + { + throw new IOException("Invalid 'Authorization' header."); + } + + authToken = authHeader.length() > authScheme.length() ? + authHeader.substring(authScheme.length() + 1) : null; + + if(authToken == null) + { + throw new IOException("Invalid 'Authorization' header."); + } + + emptyAuthHeader = false; + } + } + + protected String determineAuthScheme(String authHeader) + { + Matcher authSchemeMatcher = AUTH_SCHEME_PATTERN.matcher(authHeader); + + if(authSchemeMatcher.matches()) + { + return authSchemeMatcher.group(1); + } + + return null; + } + + /** + * @return a boolean indicating whether the authorization header was empty + */ + public boolean isEmpty() + { + return emptyAuthHeader; + } + + /** + * @param authScheme + * @return a boolean indicating whether this authorization header is attempting + * to authenticate with the provided authScheme + */ + public boolean isScheme(String authScheme) + { + return this.authScheme.equals(authScheme); + } + + protected String getScheme() + { + return authScheme; + } + + /** + * @return the authToken sent with the Authorization header in the base64 + * encoded format that it was sent + */ + public String getAuthTokenBase64() + { + return authToken; + } + + /** + * @return the authToken sent with the Authorization header as a byte array + * decoded from the base64 format that it was sent + */ + public byte[] getAuthToken() + { + return authToken != null ? Base64.decode(authToken) : null; + } +} + Index: src/main/java/org/jboss/security/negotiation/common/AuthenticationContext.java =================================================================== --- src/main/java/org/jboss/security/negotiation/common/AuthenticationContext.java (revision 0) +++ src/main/java/org/jboss/security/negotiation/common/AuthenticationContext.java (revision 0) @@ -0,0 +1,156 @@ +/* + * Created on Dec 15, 2009 + */ +package org.jboss.security.negotiation.common; + +import java.io.IOException; +import java.security.Principal; + +import org.apache.catalina.Realm; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.log4j.Logger; +import org.jboss.security.negotiation.AuthorizationHeader; + + +/** + * Maintains the context for negotiating authentication with the user. Holds + * the components necessary to perform authentication and holds the status + * of that authentication during the course of a request. + * + * @author jacob@solutionsfit.com + * @version 1.0 + */ +public class AuthenticationContext +{ + private static final Logger log = Logger.getLogger(AuthenticationContext.class); + + private Request request; + + private Response response; + private LoginConfig loginConfig; + private Realm realm; + + private AuthorizationHeader authHeader; + private String authenticationMethod; + private Principal principal; + private boolean registerPrincipal = false; + + public AuthenticationContext(final Request request, final Response response, + final LoginConfig loginConfig, final Realm realm) throws IOException + { + this.request = request; + this.response = response; + this.loginConfig = loginConfig; + this.realm = realm; + + this.principal = request.getUserPrincipal(); + this.authHeader = AuthorizationHeader.createFromRequest(request); + + if(isAuthenticated() && log.isTraceEnabled()) + { + log.trace("Already authenticated '" + this.principal.getName() + "'"); + } + } + + /** + * Returns a boolean indicating if the user has been authenticated. + * + * @return boolean + */ + public boolean isAuthenticated() + { + return principal != null; + } + + /** + * Authenticates the provided username with the provided credentials + * against the realm associated with this context. + * + * @param username + * @param credentials + */ + public void authenticate(String username, String credentials) + { + principal = realm.authenticate(username, credentials); + + if(principal != null) + { + registerPrincipal = true; + + if (log.isDebugEnabled()) + { + log.debug("authenticated principal = " + principal); + } + } + } + + /** + * Determines whether the principal should be registered by the Authenticator. + * + * @return Returns the registerPrincipal. + */ + public boolean shouldRegisterPrincipal() + { + return registerPrincipal; + } + + /** + * Get the request + * @return Returns the request. + */ + public Request getRequest() + { + return request; + } + + /** + * Get the response + * @return Returns the response. + */ + public Response getResponse() + { + return response; + } + + public Principal getPrincipal() + { + return principal; + } + + public AuthorizationHeader getAuthorizationHeader() + { + return this.authHeader; + } + + public void setAuthenticationMethod(String authenticationMethod) + { + this.authenticationMethod = authenticationMethod; + } + + public String getAuthenticationMethod() + { + return this.authenticationMethod; + } + + /** + * Get the username + * + * @return Returns the username. + */ + public String getUsername() + { + return request.getSessionInternal().getId(); + } + + /** + * Get the loginConfig + * + * @return Returns the loginConfig. + */ + public LoginConfig getLoginConfig() + { + return loginConfig; + } +} Index: src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java =================================================================== --- src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java (revision 98000) +++ src/main/java/org/jboss/security/negotiation/common/NegotiationContext.java (working copy) @@ -50,6 +50,11 @@ private Object schemeContext = null; + public static boolean isDefined() + { + return getCurrentNegotiationContext() != null; + } + public static NegotiationContext getCurrentNegotiationContext() { return negotiationContext.get(); Index: src/main/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticator.java =================================================================== --- src/main/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticator.java (revision 0) +++ src/main/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticator.java (revision 0) @@ -0,0 +1,223 @@ +/* + * Created on Dec 17, 2009 + */ +package org.jboss.security.negotiation; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.catalina.Session; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.log4j.Logger; +import org.jboss.security.negotiation.common.AuthenticationContext; +import org.jboss.security.negotiation.common.MessageTrace; +import org.jboss.security.negotiation.common.NegotiationContext; + +/** + * Authenticator that attempts to authenticate the user through HTTP + * challenge-response authentication. It first attempts to Negotiate + * authentication. If Negotiate fails, it attempts to fall back to a lesser + * authentication scheme (if available). Lesser authentication schemes can + * be provided by implementing the {@link SchemeAuthenticator} interface + * and providing the implementation when constructing an instance. + * + * @author jacob.orshalick@archongroup.com + * @version 1.0 + */ +public class DelegatingNegotiationAuthenticator +{ + private static final Logger log = Logger.getLogger(NegotiationAuthenticator.class); + + public static final String AUTH_HEADER_KEY = "WWW-Authenticate"; + + private static final String NEGOTIATE_SCHEME = "Negotiate"; + private static final String NEGOTIATION_CONTEXT = "NEGOTIATION_CONTEXT"; + + private final List fallbackSchemeAuthenticators; + + /** + * Constructs an instance with the provided list of fallbackSchemeAuthenticators. + * + * @param fallbackSchemeAuthenticators + */ + public DelegatingNegotiationAuthenticator(List fallbackSchemeAuthenticators) + { + List schemeAuthenticators = new ArrayList(fallbackSchemeAuthenticators); + this.fallbackSchemeAuthenticators = Collections.unmodifiableList(schemeAuthenticators); + } + + /** + * Attempts to negotiate authentication with the user based on the provided + * authenticationContext. If that negotiation fails, attempts to fallback to + * any provided fallbackAuthenticators. + * + * @param authenticationContext + * @return a boolean representing whether authentication was successful + * @throws IOException + */ + public boolean authenticate(AuthenticationContext authenticationContext) + throws IOException + { + if (authenticationContext.isAuthenticated()) + { + return true; + } + + AuthorizationHeader authHeader = authenticationContext.getAuthorizationHeader(); + LoginConfig config = authenticationContext.getLoginConfig(); + Request request = authenticationContext.getRequest(); + Response response = authenticationContext.getResponse(); + + boolean authenticated = false; + + if(authHeader.isEmpty()) + { + sendErrorWithSupportedSchemes(response, config); + } + else if(authHeader.isScheme(NEGOTIATE_SCHEME)) + { + authenticated = negotiateAuthentication(authenticationContext); + } + else + { + SchemeAuthenticator schemeAuthenticator = findFallbackSchemeAuthenticator(authHeader); + authenticated = schemeAuthenticator.authenticate(request, response, authenticationContext); + } + + return authenticated; + } + + protected SchemeAuthenticator findFallbackSchemeAuthenticator(AuthorizationHeader authHeader) throws IOException + { + for(SchemeAuthenticator schemeAuthenticator : fallbackSchemeAuthenticators) + { + if(authHeader.isScheme(schemeAuthenticator.getScheme())) + { + return schemeAuthenticator; + } + } + + throw new IOException("Unsupported authorization scheme"); + } + + protected void sendErrorWithSupportedSchemes(Response response, LoginConfig loginConfig) throws IOException + { + log.debug("No Authorization Header, sending 401"); + + log.debug(AUTH_HEADER_KEY + ": " + NEGOTIATE_SCHEME); + response.setHeader(AUTH_HEADER_KEY, NEGOTIATE_SCHEME); + + sendSupportedFallbackSchemes(response, loginConfig); + + response.sendError(401); + } + + protected void sendSupportedFallbackSchemes(Response response, LoginConfig loginConfig) + { + for(SchemeAuthenticator schemeAuthenticator : fallbackSchemeAuthenticators) + { + String supportedSchemeHeader = schemeAuthenticator.getAuthenticateHeader(loginConfig); + + log.debug(AUTH_HEADER_KEY + ": " + supportedSchemeHeader); + response.addHeader(AUTH_HEADER_KEY, supportedSchemeHeader); + } + } + + /** + * Attempts to negotiate authentication with the user based on the + * private authenticationContext. + * + * @param authenticationContext + * @return a boolean indicating if authentication was negotiated successfully + * @throws IOException + */ + protected boolean negotiateAuthentication(final AuthenticationContext authenticationContext) throws IOException + { + log.trace("Authenticating user"); + + Request request = authenticationContext.getRequest(); + Response response = authenticationContext.getResponse(); + AuthorizationHeader authHeader = authenticationContext.getAuthorizationHeader(); + + byte[] authToken = authHeader.getAuthToken(); + ByteArrayInputStream authTokenIS = new ByteArrayInputStream(authToken); + MessageTrace.logRequestBase64(authHeader.getAuthTokenBase64()); + MessageTrace.logRequestHex(authToken); + + Session session = request.getSessionInternal(); + NegotiationContext negotiationContext = (NegotiationContext) session.getNote(NEGOTIATION_CONTEXT); + if (negotiationContext == null) + { + log.debug("Creating new NegotiationContext"); + { + negotiationContext = new NegotiationContext(); + session.setNote(NEGOTIATION_CONTEXT, negotiationContext); + } + } + + try + { + // Set the ThreadLocal association. + negotiationContext.associate(); + + MessageFactory mf = MessageFactory.newInstance(); + if (mf.accepts(authTokenIS) == false) + { + throw new IOException("Unsupported negotiation mechanism."); + } + + NegotiationMessage requestMessage = mf.createMessage(authTokenIS); + negotiationContext.setRequestMessage(requestMessage); + + authenticationContext.authenticate(authenticationContext.getUsername(), (String) null); + authenticationContext.setAuthenticationMethod(negotiationContext.getAuthenticationMethod()); + + NegotiationMessage responseMessage = negotiationContext.getResponseMessage(); + + if (responseMessage != null) + { + ByteArrayOutputStream responseMessageOS = new ByteArrayOutputStream(); + responseMessage.writeTo(responseMessageOS, true); + String responseHeader = responseMessageOS.toString(); + + MessageTrace.logResponseBase64(responseHeader); + + response.setHeader(AUTH_HEADER_KEY, + NEGOTIATE_SCHEME + " " + responseHeader); + } + else if(!authenticationContext.isAuthenticated()) + { + // negotiation authentication failed, so attempt fallback + log.info("Negotiation failed due to unsupported negotiate message or Kerberos failure. " + + "Attempting to fallback to lesser authentication mechanism."); + + sendSupportedFallbackSchemes(response, authenticationContext.getLoginConfig()); + } + } + catch (NegotiationException e) + { + IOException ioe = new IOException("Error processing " + NEGOTIATE_SCHEME + " header."); + ioe.initCause(e); + throw ioe; + } + finally + { + // Clear the headers and remove the ThreadLocal association. + negotiationContext.clear(); + } + + if (!authenticationContext.isAuthenticated()) + { + response.sendError(Response.SC_UNAUTHORIZED); + } + + return authenticationContext.isAuthenticated(); + } +} Index: src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java =================================================================== --- src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java (revision 98000) +++ src/main/java/org/jboss/security/negotiation/NegotiationAuthenticator.java (working copy) @@ -22,21 +22,14 @@ */ package org.jboss.security.negotiation; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.security.Principal; - -import org.apache.catalina.Realm; -import org.apache.catalina.Session; +import java.util.Collections; import org.apache.catalina.authenticator.AuthenticatorBase; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; import org.apache.log4j.Logger; -import org.jboss.security.negotiation.common.MessageTrace; -import org.jboss.security.negotiation.common.NegotiationContext; -import org.jboss.util.Base64; +import org.jboss.security.negotiation.common.AuthenticationContext; /** * An authenticator to manage Negotiation based authentication in connection with the @@ -47,124 +40,33 @@ */ public class NegotiationAuthenticator extends AuthenticatorBase { - private static final Logger log = Logger.getLogger(NegotiationAuthenticator.class); - private static final String NEGOTIATE = "Negotiate"; - - private static final String NEGOTIATION_CONTEXT = "NEGOTIATION_CONTEXT"; - - protected String getNegotiateScheme() + private final DelegatingNegotiationAuthenticator delegatingNegotiationAuthenticator; + + public NegotiationAuthenticator() { - return NEGOTIATE; + super(); + + delegatingNegotiationAuthenticator = new DelegatingNegotiationAuthenticator(Collections.EMPTY_LIST); } - + @Override protected boolean authenticate(final Request request, final Response response, final LoginConfig config) throws IOException { - log.trace("Authenticating user"); - - Principal principal = request.getUserPrincipal(); - if (principal != null) - { - if (log.isTraceEnabled()) - log.trace("Already authenticated '" + principal.getName() + "'"); - return true; - } - - String negotiateScheme = getNegotiateScheme(); - - log.debug("Header - " + request.getHeader("Authorization")); - String authHeader = request.getHeader("Authorization"); - if (authHeader == null) - { - log.debug("No Authorization Header, sending 401"); - response.setHeader("WWW-Authenticate", negotiateScheme); - response.sendError(401); - - return false; - } - else if (authHeader.startsWith(negotiateScheme + " ") == false) - { - throw new IOException("Invalid 'Authorization' header."); - } - - String authTokenBase64 = authHeader.substring(negotiateScheme.length() + 1); - byte[] authToken = Base64.decode(authTokenBase64); - ByteArrayInputStream authTokenIS = new ByteArrayInputStream(authToken); - MessageTrace.logRequestBase64(authTokenBase64); - MessageTrace.logRequestHex(authToken); - - Session session = request.getSessionInternal(); - NegotiationContext negotiationContext = (NegotiationContext) session.getNote(NEGOTIATION_CONTEXT); - if (negotiationContext == null) - { - log.debug("Creating new NegotiationContext"); - { - negotiationContext = new NegotiationContext(); - session.setNote(NEGOTIATION_CONTEXT, negotiationContext); - } - } - - String username = session.getId(); - String authenticationMethod = ""; - try - { - // Set the ThreadLocal association. - negotiationContext.associate(); - - MessageFactory mf = MessageFactory.newInstance(); - if (mf.accepts(authTokenIS) == false) - { - throw new IOException("Unsupported negotiation mechanism."); - } - - NegotiationMessage requestMessage = mf.createMessage(authTokenIS); - negotiationContext.setRequestMessage(requestMessage); - - Realm realm = context.getRealm(); - principal = realm.authenticate(username, (String) null); - - authenticationMethod = negotiationContext.getAuthenticationMethod(); - - if (log.isDebugEnabled() && principal != null) - log.debug("authenticated principal = " + principal); - - NegotiationMessage responseMessage = negotiationContext.getResponseMessage(); - if (responseMessage != null) - { - ByteArrayOutputStream responseMessageOS = new ByteArrayOutputStream(); - responseMessage.writeTo(responseMessageOS, true); - String responseHeader = responseMessageOS.toString(); - - MessageTrace.logResponseBase64(responseHeader); - - response.setHeader("WWW-Authenticate", negotiateScheme + " " + responseHeader); - } - - } - catch (NegotiationException e) - { - IOException ioe = new IOException("Error processing " + negotiateScheme + " header."); - ioe.initCause(e); - throw ioe; - } - finally - { - // Clear the headers and remove the ThreadLocal association. - negotiationContext.clear(); - } - - if (principal == null) - { - response.sendError(Response.SC_UNAUTHORIZED); - } - else - { - register(request, response, principal, authenticationMethod, username, null); - } - - return (principal != null); + AuthenticationContext authenticationContext = + new AuthenticationContext(request, response, config, context.getRealm()); + + boolean authenticated = delegatingNegotiationAuthenticator.authenticate(authenticationContext); + + if(authenticationContext.shouldRegisterPrincipal()) + { + register(request, response, authenticationContext.getPrincipal(), + authenticationContext.getAuthenticationMethod(), + authenticationContext.getUsername(), null); + } + + return authenticated; } } Index: src/main/java/org/jboss/security/negotiation/NegotiationWithBasicFallbackAuthenticator.java =================================================================== --- src/main/java/org/jboss/security/negotiation/NegotiationWithBasicFallbackAuthenticator.java (revision 0) +++ src/main/java/org/jboss/security/negotiation/NegotiationWithBasicFallbackAuthenticator.java (revision 0) @@ -0,0 +1,91 @@ +/* + * Created on Dec 15, 2009 + */ +package org.jboss.security.negotiation; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.catalina.authenticator.BasicAuthenticator; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.log4j.Logger; +import org.jboss.security.negotiation.common.AuthenticationContext; + +/** + * Attempts SPNEGO authentication and if that authentication fails, falls back + * to Basic authentication. + * + * @author jacob@solutionsfit.com + * @version 1.0 + */ +public class NegotiationWithBasicFallbackAuthenticator extends BasicAuthenticator implements SchemeAuthenticator +{ + private static final Logger log = Logger.getLogger(NegotiationAuthenticator.class); + + private final DelegatingNegotiationAuthenticator delegatingNegotiationAuthenticator; + + public NegotiationWithBasicFallbackAuthenticator() + { + super(); + + List fallbackAuthenticators = new ArrayList(); + fallbackAuthenticators.add(this); + + delegatingNegotiationAuthenticator = new DelegatingNegotiationAuthenticator(fallbackAuthenticators); + } + + @Override + public boolean authenticate(final Request request, final Response response, final LoginConfig config) + throws IOException + { + AuthenticationContext authenticationContext = + new AuthenticationContext(request, response, config, context.getRealm()); + + boolean authenticated = delegatingNegotiationAuthenticator.authenticate(authenticationContext); + + if(authenticationContext.shouldRegisterPrincipal()) + { + register(request, response, authenticationContext.getPrincipal(), + authenticationContext.getAuthenticationMethod(), + authenticationContext.getUsername(), null); + } + + return authenticated; + } + + /** + * @see org.jboss.security.negotiation.SchemeAuthenticator#authenticate(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response, org.jboss.security.negotiation.common.AuthenticationContext) + * @param request + * @param response + * @param authenticationContext + * @return + * @throws IOException + */ + public boolean authenticate(Request request, Response response, + AuthenticationContext authenticationContext) throws IOException + { + return super.authenticate(request, response, authenticationContext.getLoginConfig()); + } + + /** + * @see org.jboss.security.negotiation.SchemeAuthenticator#getAuthenticateHeader(org.apache.catalina.deploy.LoginConfig) + * @param loginConfig + * @return + */ + public String getAuthenticateHeader(LoginConfig loginConfig) + { + return getScheme() + " " + loginConfig.getRealmName(); + } + + /** + * @see org.jboss.security.negotiation.SchemeAuthenticator#getScheme() + * @return + */ + public String getScheme() + { + return "Basic"; + } +} Index: src/main/java/org/jboss/security/negotiation/SchemeAuthenticator.java =================================================================== --- src/main/java/org/jboss/security/negotiation/SchemeAuthenticator.java (revision 0) +++ src/main/java/org/jboss/security/negotiation/SchemeAuthenticator.java (revision 0) @@ -0,0 +1,31 @@ +/* + * Created on Dec 15, 2009 + */ +package org.jboss.security.negotiation; + +import java.io.IOException; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.deploy.LoginConfig; +import org.jboss.security.negotiation.common.AuthenticationContext; + +/** + * Represents an authenticator for a particular scheme (e.g. Digest, Basic) that + * can be added as a fallback authenticator for the + * {@link DelegatingNegotiationAuthenticator}. Fallback authenticators + * can be provided by implementing this interface and delegating authentication + * from an Authenticator to the {@link DelegatingNegotiationAuthenticator}. + * + * @author jacob@solutionsfit.com + * @version 1.0 + */ +public interface SchemeAuthenticator +{ + public String getScheme(); + + public String getAuthenticateHeader(LoginConfig loginConfig); + + public boolean authenticate(final Request request, final Response response, final AuthenticationContext authenticationContext) + throws IOException; +} Index: src/tests/java/AuthenticationContextTest.java =================================================================== --- src/tests/java/AuthenticationContextTest.java (revision 0) +++ src/tests/java/AuthenticationContextTest.java (revision 0) @@ -0,0 +1,98 @@ +import static org.easymock.EasyMock.*; + +import java.io.IOException; +import java.security.Principal; + +import org.apache.catalina.Realm; +import org.apache.catalina.connector.Request; +import org.jboss.security.negotiation.common.AuthenticationContext; + +import junit.framework.TestCase; + +/** + * + * @author jacob.orshalick@archongroup.com + * @version 1.0 + */ +public class AuthenticationContextTest extends TestCase +{ + private AuthenticationContext authenticationContext; + + private Request mockRequest; + private Realm mockRealm; + private Principal mockPrincipal; + + @Override + public void setUp() throws IOException + { + mockPrincipal = createMock(Principal.class); + mockRealm = createMock(Realm.class); + mockRequest = new Request() + { + @Override + public Principal getUserPrincipal() + { + return mockPrincipal; + } + + @Override + public String getHeader(String name) + { + return "Basic asdfjjsadhafsj"; + } + }; + } + + public void testIsAlreadyAuthenticated() throws IOException + { + AuthenticationContext authenticationContext = + new AuthenticationContext(mockRequest, null, null, null); + + assertTrue(authenticationContext.isAuthenticated()); + } + + public void testIsAlreadyAuthenticated_NotAuthenticatedYet() throws IOException + { + mockPrincipal = null; + + AuthenticationContext authenticationContext = + new AuthenticationContext(mockRequest, null, null, null); + + assertFalse(authenticationContext.isAuthenticated()); + } + + public void testAuthenticate_Success() throws IOException + { + AuthenticationContext authenticationContext = + new AuthenticationContext(mockRequest, null, null, mockRealm); + + expect(mockRealm.authenticate("testUser", "testCredentials")).andReturn(mockPrincipal); + + replay(mockRealm); + + authenticationContext.authenticate("testUser", "testCredentials"); + + verify(mockRealm); + + assertTrue(authenticationContext.shouldRegisterPrincipal()); + assertTrue(authenticationContext.isAuthenticated()); + } + + + public void testAuthenticate_Failure() throws IOException + { + AuthenticationContext authenticationContext = + new AuthenticationContext(mockRequest, null, null, mockRealm); + + expect(mockRealm.authenticate("testUser", "testCredentials")).andReturn(null); + + replay(mockRealm); + + authenticationContext.authenticate("testUser", "testCredentials"); + + verify(mockRealm); + + assertFalse(authenticationContext.shouldRegisterPrincipal()); + assertFalse(authenticationContext.isAuthenticated()); + } +} Index: src/tests/java/org/jboss/security/negotiation/AuthorizationHeaderTest.java =================================================================== --- src/tests/java/org/jboss/security/negotiation/AuthorizationHeaderTest.java (revision 0) +++ src/tests/java/org/jboss/security/negotiation/AuthorizationHeaderTest.java (revision 0) @@ -0,0 +1,72 @@ +/* + * Created on Dec 14, 2009 + */ +package org.jboss.security.negotiation; + +import java.io.IOException; + +import org.jboss.security.negotiation.AuthorizationHeader; +import org.jboss.util.Base64; + +import junit.framework.TestCase; + +/** + * + * @author jacob@solutionsfit.com + * @version 1.0 + */ +public class AuthorizationHeaderTest extends TestCase +{ + private AuthorizationHeader header; + private String headerToken; + private String encodedHeaderToken; + private String headerString; + + @Override + public void setUp() throws IOException + { + headerToken = "hello world"; + encodedHeaderToken = Base64.encodeBytes(headerToken.getBytes()); + headerString = "Negotiate " + encodedHeaderToken; + header = new AuthorizationHeader(headerString); + } + + public void testGetAuthToken() + { + assertEquals(headerToken, new String(header.getAuthToken())); + } + + public void testConstruct_NullHeader() throws IOException + { + header = new AuthorizationHeader(null); + + assertTrue(header.isEmpty()); + } + + public void testConstruct_EmptyHeader() throws IOException + { + header = new AuthorizationHeader(""); + + assertTrue(header.isEmpty()); + } + + public void testConstruct_NoToken() throws IOException + { + try + { + header = new AuthorizationHeader("Negotiate"); + fail("Should not allow an auth scheme with no token"); + } + catch(IOException e) + { + // do nothing, expected + } + } + + public void testDetermineAuthScheme() + { + String authScheme = header.determineAuthScheme(headerString); + + assertEquals("Negotiate", authScheme); + } +} Index: src/tests/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticatorTest.java =================================================================== --- src/tests/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticatorTest.java (revision 0) +++ src/tests/java/org/jboss/security/negotiation/DelegatingNegotiationAuthenticatorTest.java (revision 0) @@ -0,0 +1,89 @@ +/* + * Created on Dec 18, 2009 + */ +package org.jboss.security.negotiation; + +import static org.easymock.EasyMock.*; + +import java.io.IOException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.catalina.connector.Request; +import org.jboss.util.Base64; + +import junit.framework.TestCase; + +/** + * + * @author jacob.orshalick@archongroup.com + * @version 1.0 + */ +public class DelegatingNegotiationAuthenticatorTest extends TestCase +{ + private DelegatingNegotiationAuthenticator authenticator; + private AuthorizationHeader authHeader; + private List fallbackAuthenticators; + + private SchemeAuthenticator mockFallbackSchemeAuthenticator; + + private String headerToken; + private String encodedHeaderToken; + private String headerString; + + @Override + public void setUp() throws IOException + { + mockFallbackSchemeAuthenticator = createMock(SchemeAuthenticator.class); + + headerToken = "hello:world"; + encodedHeaderToken = Base64.encodeBytes(headerToken.getBytes()); + authHeader = new AuthorizationHeader("Basic " + encodedHeaderToken); + + fallbackAuthenticators = new ArrayList(); + fallbackAuthenticators.add(mockFallbackSchemeAuthenticator); + + authenticator = new DelegatingNegotiationAuthenticator(fallbackAuthenticators); + } + + public void testFindFallbackSchemeAuthenticator_NoFallbacks() + { + fallbackAuthenticators.clear(); + + try + { + authenticator.findFallbackSchemeAuthenticator(authHeader); + } + catch(IOException e) + { + // do nothing, expected result + } + } + + public void testFindFallbackSchemeAuthenticator_NoneFound() throws IOException + { + authHeader = new AuthorizationHeader("Digest " + encodedHeaderToken); + + try + { + authenticator.findFallbackSchemeAuthenticator(authHeader); + } + catch(IOException e) + { + // do nothing, expected result + } + } + + public void testFindFallbackSchemeAuthenticator_Basic() throws IOException + { + expect(mockFallbackSchemeAuthenticator.getScheme()).andReturn("Basic"); + + replay(mockFallbackSchemeAuthenticator); + + assertSame(mockFallbackSchemeAuthenticator, authenticator.findFallbackSchemeAuthenticator(authHeader)); + + verify(mockFallbackSchemeAuthenticator); + } +}