package org.limewire.http.auth; import java.io.IOException; import org.apache.http.Header; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.nio.entity.BufferingNHttpEntity; import org.apache.http.nio.entity.ConsumingNHttpEntity; import org.apache.http.nio.protocol.NHttpRequestHandler; import org.apache.http.nio.protocol.NHttpResponseTrigger; import org.apache.http.nio.util.DirectByteBufferAllocator; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.jmock.Expectations; import org.jmock.Mockery; import org.limewire.util.BaseTestCase; public class AuthenticationInterceptorImplTest extends BaseTestCase { private Mockery mockery; private Authenticator authenticator; private ProtectedHandler protectedHandler; private AuthenticationInterceptorImpl requestAuthenticatorImpl; private NHttpRequestHandler guardedHandler; @Override protected void setUp() throws Exception { mockery = new Mockery(); authenticator = mockery.mock(Authenticator.class); protectedHandler = new ProtectedHandler(); requestAuthenticatorImpl = new AuthenticationInterceptorImpl(authenticator); guardedHandler = requestAuthenticatorImpl.getGuardedHandler("/", protectedHandler); } public AuthenticationInterceptorImplTest(String name) { super(name); } public void testProcessNoCredentials() throws Exception { mockery.checking(new Expectations() {{ never(authenticator).authenticate(with(any(Credentials.class))); }}); protectedHandler.setShouldBeCalled(Method.NONE); HttpRequest request = new BasicHttpRequest("GET", "/"); runTest(request, createResponse(1, "WWW-Authenticate: Basic realm=\"secure\"", 401)); } public void testProcessEntityRequestNoCredentials() throws Exception { mockery.checking(new Expectations() {{ never(authenticator).authenticate(with(any(Credentials.class))); }}); protectedHandler.setShouldBeCalled(Method.NONE); runTest(new BasicHttpEntityEnclosingRequest("GET", "/"), false); } public void testGuardedHandlerDoesNotGuard() { Unprotectedhandler handler = new Unprotectedhandler(); assertSame(handler, requestAuthenticatorImpl.getGuardedHandler("blakjdlkjb", handler)); } public void testUnprotectedHandler() throws Exception { Unprotectedhandler handler = new Unprotectedhandler(); NHttpRequestHandler guardedHandler = requestAuthenticatorImpl.getGuardedHandler("/test/", handler); HttpRequest request = new BasicHttpRequest("GET", "/test/"); // this one on the other hand should be called handler.setShouldBeCalled(Method.HANDLE); BasicHttpContext context = new BasicHttpContext(); final NHttpResponseTrigger trigger = mockery.mock(NHttpResponseTrigger.class); mockery.checking(new Expectations() {{ allowing(trigger).submitResponse(with(any(HttpResponse.class))); }}); requestAuthenticatorImpl.process(request, context); guardedHandler.handle(request, createResponse(0, "", 200), trigger, context); mockery.assertIsSatisfied(); handler.assertIsSatisfied(); } public void testProcessWrongCredentials() throws Exception { mockery.checking(new Expectations() {{ one(authenticator).authenticate(with(new BaseMatcher<Credentials>() { @Override public boolean matches(Object item) { Credentials credentials = (Credentials)item; assertEquals("hello", credentials.getUserPrincipal().getName()); assertEquals("world", credentials.getPassword()); return true; } @Override public void describeTo(Description description) { } })); will(returnValue(false)); }}); protectedHandler.setShouldBeCalled(Method.NONE); runTest(createRequest("hello", "world"), createResponse(1, "WWW-Authenticate: Basic realm=\"secure\"", 401)); } public void testProcessValidCredentials() throws Exception { mockery.checking(new Expectations() {{ one(authenticator).authenticate(with(new BaseMatcher<Credentials>() { @Override public boolean matches(Object item) { Credentials credentials = (Credentials)item; assertEquals("hello", credentials.getUserPrincipal().getName()); assertEquals("world", credentials.getPassword()); return true; } @Override public void describeTo(Description description) { } })); will(returnValue(true)); }}); protectedHandler.setShouldBeCalled(Method.HANDLE); runTest(createRequest("hello", "world"), createResponse(0, "", 200)); } public void testProcessValidCredentialsForEntityEnclosingRequest() throws Exception { mockery.checking(new Expectations() {{ one(authenticator).authenticate(with(new BaseMatcher<Credentials>() { @Override public boolean matches(Object item) { Credentials credentials = (Credentials)item; assertEquals("hello", credentials.getUserPrincipal().getName()); assertEquals("world", credentials.getPassword()); return true; } @Override public void describeTo(Description description) { } })); will(returnValue(true)); }}); protectedHandler.setShouldBeCalled(Method.ENTITY_REQUEST); runTest(createEntityEnclosingRequest("hello", "world"), true); } private HttpResponse createResponse(final int times, final String header, final int code) { final HttpResponse response = mockery.mock(HttpResponse.class); mockery.checking(new Expectations() {{ exactly(times).of(response).addHeader(with(new BaseMatcher<Header>() { @Override public boolean matches(Object item) { return item.toString().equals(header); } @Override public void describeTo(Description description) { } })); exactly(times).of(response).setStatusCode(code); }}); return response; } private void runTest(HttpRequest request, HttpResponse response) throws Exception { BasicHttpContext context = new BasicHttpContext(); final NHttpResponseTrigger trigger = mockery.mock(NHttpResponseTrigger.class); mockery.checking(new Expectations() {{ allowing(trigger).submitResponse(with(any(HttpResponse.class))); }}); requestAuthenticatorImpl.process(request, context); guardedHandler.handle(request, response, trigger, context); assertEquals(protectedHandler.shouldBeCalled, protectedHandler.called); mockery.assertIsSatisfied(); } private void runTest(HttpEntityEnclosingRequest request, boolean shouldProduceEntityConsumer) throws Exception { BasicHttpContext context = new BasicHttpContext(); requestAuthenticatorImpl.process(request, context); ConsumingNHttpEntity entity = guardedHandler.entityRequest(request, context); assertTrue((entity != null) == shouldProduceEntityConsumer); assertEquals(protectedHandler.shouldBeCalled, protectedHandler.called); mockery.assertIsSatisfied(); } private Header createAuthorizationHeader(String username, String password) { UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); return BasicScheme.authenticate(credentials, "UTF-8", false); } private HttpRequest createRequest(String username, String password) { BasicHttpRequest request = new BasicHttpRequest("GET", "/"); request.addHeader(createAuthorizationHeader(username, password)); return request; } private HttpEntityEnclosingRequest createEntityEnclosingRequest(String username, String password) { BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("GET", "/"); request.addHeader(createAuthorizationHeader(username, password)); return request; } private class Unprotectedhandler extends Handler { } @RequiresAuthentication private class ProtectedHandler extends Handler { @Override public ConsumingNHttpEntity entityRequest(HttpEntityEnclosingRequest request, HttpContext context) throws HttpException, IOException { ConsumingNHttpEntity entity = super.entityRequest(request, context); ServerAuthState serverAuthState = (ServerAuthState)context.getAttribute(ServerAuthState.AUTH_STATE); assertNotNull(serverAuthState.getCredentials()); return entity; } @Override public void handle(HttpRequest arg0, HttpResponse arg1, NHttpResponseTrigger arg2, HttpContext arg3) throws HttpException, IOException { // call super for setting common state super.handle(arg0, arg1, arg2, arg3); ServerAuthState serverAuthState = (ServerAuthState)arg3.getAttribute(ServerAuthState.AUTH_STATE); assertNotNull(serverAuthState.getCredentials()); } } enum Method {ENTITY_REQUEST, HANDLE, NONE} private class Handler implements NHttpRequestHandler { Method shouldBeCalled; Method called = Method.NONE; public void setShouldBeCalled(Method shouldBeCalled) { this.shouldBeCalled = shouldBeCalled; this.called = Method.NONE; } @Override public ConsumingNHttpEntity entityRequest(HttpEntityEnclosingRequest request, HttpContext context) throws HttpException, IOException { assertNotNull(context.getAttribute(ServerAuthState.AUTH_STATE)); called = Method.ENTITY_REQUEST; return new BufferingNHttpEntity(new BasicHttpEntity(), new DirectByteBufferAllocator()); } @Override public void handle(HttpRequest request, HttpResponse response, NHttpResponseTrigger trigger, HttpContext context) throws HttpException, IOException { assertNotNull(context.getAttribute(ServerAuthState.AUTH_STATE)); called = Method.HANDLE; } public boolean assertIsSatisfied() { return shouldBeCalled == called; } } }