/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.resteasy.filter; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.candlepin.auth.NoAuthPrincipal; import org.candlepin.auth.Principal; import org.candlepin.auth.UserPrincipal; import org.candlepin.auth.permissions.PermissionFactory; import org.candlepin.common.auth.SecurityHole; import org.candlepin.common.exceptions.BadRequestException; import org.candlepin.common.exceptions.NotAuthorizedException; import org.candlepin.config.ConfigProperties; import org.candlepin.model.ConsumerCurator; import org.candlepin.model.DeletedConsumerCurator; import org.candlepin.model.User; import org.candlepin.service.UserServiceAdapter; import org.candlepin.test.DatabaseTestFixture; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.Provides; import org.jboss.resteasy.core.ResourceMethodInvoker; import org.jboss.resteasy.core.interception.PostMatchContainerRequestContext; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jukito.JukitoRunner; import org.jukito.TestSingleton; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.lang.reflect.Method; import javax.inject.Inject; import javax.inject.Provider; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ResourceInfo; /** * AuthInterceptorTest */ @RunWith(JukitoRunner.class) public class AuthenticationFilterTest extends DatabaseTestFixture { @Inject private DeletedConsumerCurator deletedConsumerCurator; @Inject private Provider<UserServiceAdapter> usaProvider; @Inject private Injector injector; @Mock private HttpServletRequest mockHttpServletRequest; @Mock private ContainerRequestContext mockRequestContext; @Mock private CandlepinSecurityContext mockSecurityContext; @Mock private ResourceInfo mockInfo; private UserServiceAdapter usa; private AuthenticationFilter interceptor; private MockHttpRequest mockReq; protected Module getGuiceOverrideModule() { return new AuthInterceptorTestModule(); } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); usa = usaProvider.get(); Class clazz = FakeResource.class; when(mockInfo.getResourceClass()).thenReturn(clazz); mockReq = MockHttpRequest.create("GET", "http://localhost/candlepin/status"); ResteasyProviderFactory.pushContext(ResourceInfo.class, mockInfo); ResteasyProviderFactory.pushContext(HttpRequest.class, mockReq); when(mockRequestContext.getSecurityContext()).thenReturn(mockSecurityContext); config.setProperty(ConfigProperties.OAUTH_AUTHENTICATION, "false"); config.setProperty(ConfigProperties.SSL_AUTHENTICATION, "false"); config.setProperty(ConfigProperties.BASIC_AUTHENTICATION, "true"); config.setProperty(ConfigProperties.TRUSTED_AUTHENTICATION, "true"); interceptor = new AuthenticationFilter(config, consumerCurator, deletedConsumerCurator, injector); interceptor.setHttpServletRequest(mockHttpServletRequest); } private void mockResourceMethod(Method method) { when(mockInfo.getResourceMethod()).thenReturn(method); } private ContainerRequestContext getContext() { return getContext(null); } private ContainerRequestContext getContext(ResourceMethodInvoker invoker) { return new PostMatchContainerRequestContext(mockReq, invoker); } @Test(expected = BadRequestException.class) public void noSecurityHoleNoPrincipalNoSsl() throws Exception { when(mockHttpServletRequest.isSecure()).thenReturn(false); Method method = FakeResource.class.getMethod("someMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); } @Test(expected = NotAuthorizedException.class) public void noSecurityHoleNoPrincipalNoSslButOverridenByConfig() throws Exception { config.setProperty(ConfigProperties.AUTH_OVER_HTTP, "true"); try { when(mockHttpServletRequest.isSecure()).thenReturn(false); Method method = FakeResource.class.getMethod("someMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); } finally { /** * Revert default settings */ config.setProperty(ConfigProperties.AUTH_OVER_HTTP, "false"); } } @Test(expected = NotAuthorizedException.class) public void noSecurityHoleNoPrincipal() throws Exception { when(mockHttpServletRequest.isSecure()).thenReturn(true); Method method = FakeResource.class.getMethod("someMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); } @Test public void noSecurityHole() throws Exception { mockReq.header("Authorization", "BASIC QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); when(usa.validateUser(eq("Aladdin"), eq("open sesame"))).thenReturn(true); when(usa.findByLogin(eq("Aladdin"))).thenReturn( new User("Aladdin", "open sesame", true)); Method method = FakeResource.class.getMethod("someMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); Principal p = ResteasyProviderFactory.getContextData(Principal.class); assertTrue(p instanceof UserPrincipal); } @Test public void securityHoleWithNoAuth() throws Exception { Method method = FakeResource.class.getMethod("noAuthMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); Principal p = ResteasyProviderFactory.getContextData(Principal.class); assertTrue(p instanceof NoAuthPrincipal); } @Test public void securityHoleWithAuth() throws Exception { Method method = FakeResource.class.getMethod("annotatedMethod", String.class); mockResourceMethod(method); mockReq.header("Authorization", "BASIC QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); when(usa.validateUser(eq("Aladdin"), eq("open sesame"))).thenReturn(true); when(usa.findByLogin(eq("Aladdin"))).thenReturn(new User("Aladdin", "open sesame")); interceptor.filter(getContext()); Principal p = ResteasyProviderFactory.getContextData(Principal.class); assertTrue(p instanceof UserPrincipal); } @Test public void securityHoleWithAnonAndNoPrincipal() throws Exception { Method method = FakeResource.class.getMethod("anonMethod", String.class); mockResourceMethod(method); interceptor.filter(getContext()); Principal p = ResteasyProviderFactory.getContextData(Principal.class); assertTrue(p instanceof NoAuthPrincipal); // Anon should not even bother attempting to create a real principal verify(usa, times(0)).validateUser(anyString(), anyString()); } @Test public void securityHoleWithAnonAndPrincipalProvided() throws Exception { Method method = FakeResource.class.getMethod("anonMethod", String.class); mockResourceMethod(method); mockReq.header("Authorization", "BASIC QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); interceptor.filter(getContext()); Principal p = ResteasyProviderFactory.getContextData(Principal.class); assertTrue(p instanceof NoAuthPrincipal); // Anon should not even bother attempting to create a real principal verify(usa, times(0)).validateUser(anyString(), anyString()); } /** * FakeResource simply to create a Method object to pass down into * the interceptor. */ public static class FakeResource { public String someMethod(String str) { return str; } @SecurityHole public String annotatedMethod(String str) { return str; } @SecurityHole(noAuth = true) public String noAuthMethod(String str) { return str; } @SecurityHole(anon = true) public String anonMethod(String str) { return str; } } private static class AuthInterceptorTestModule extends AbstractModule { @Override protected void configure() { bind(PermissionFactory.class).toInstance(mock(PermissionFactory.class)); bind(ConsumerCurator.class).toInstance(mock(ConsumerCurator.class)); } @Provides @TestSingleton protected UserServiceAdapter getAdapter() { return mock(UserServiceAdapter.class); } } }