/* * Copyright (C) 2012 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.bonitasoft.console.common.server.login.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; import org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.TenantIdAccessor; import org.bonitasoft.console.common.server.login.localization.Locator; import org.bonitasoft.console.common.server.login.localization.RedirectUrl; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * Created by Vincent Elcrin * Date: 28/08/13 * Time: 17:52 */ public class AuthenticationFilterTest { private static final String excludeAuthenticationPattern = "^/(bonita/)?((mobile/)?login.jsp$)|(images/)|(loginservice)|(serverAPI)|(/mobile/js/)|(maintenance.jsp$)|(API/platform/)|(platformloginservice$)|(portal/themeResource$)|(portal/scripts)|(portal/formsService)|(/bonita/?$)|(logoutservice)"; @Mock private FilterChain chain; @Mock private HttpServletRequestAccessor request; @Mock private HttpServletRequest httpRequest; @Mock private HttpServletResponse httpResponse; @Mock private HttpServletResponse response; @Mock private TenantIdAccessor tenantIdAccessor; @Mock private HttpSession httpSession; @Spy AuthenticationFilter authenticationFilter; @Before public void setUp() throws Exception { initMocks(this); doReturn(httpSession).when(request).getHttpSession(); when(request.asHttpServletRequest()).thenReturn(httpRequest); doReturn(new FakeAuthenticationManager(1L)).when(authenticationFilter).getAuthenticationManager(any(TenantIdAccessor.class)); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer()); when(request.getTenantId()).thenReturn("1"); } @Test public void testIfWeGoThroughFilterWhenAtLeastOneRulePass() throws Exception { authenticationFilter.addRule(createPassingRule()); authenticationFilter.addRule(createFailingRule()); authenticationFilter.doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); } @Test public void testIfWeAreNotRedirectedIfAtLeastOneRulePass() throws Exception { authenticationFilter.addRule(createFailingRule()); authenticationFilter.addRule(createPassingRule()); authenticationFilter.doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(response, never()).sendRedirect(anyString()); } @Test public void testIfWeAreRedirectedIfAllRulesFail() throws Exception { authenticationFilter.addRule(createFailingRule()); authenticationFilter.addRule(createFailingRule()); when(httpRequest.getContextPath()).thenReturn("/bonita"); when(httpRequest.getPathInfo()).thenReturn("/portal"); authenticationFilter.doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(response).sendRedirect(anyString()); } @Test public void testIfWeDontGoThroughTheChainWhenRulesFails() throws Exception { authenticationFilter.addRule(createFailingRule()); authenticationFilter.addRule(createFailingRule()); when(httpRequest.getContextPath()).thenReturn("/bonita"); when(httpRequest.getPathInfo()).thenReturn("/portal"); authenticationFilter.doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); } @Test public void testIfTenantIdIsAddedToRedirectUrlWhenInRequest() throws Exception { authenticationFilter.addRule(createFailingRule()); doReturn("12").when(request).getTenantId(); when(httpRequest.getContextPath()).thenReturn("/bonita"); when(httpRequest.getPathInfo()).thenReturn("/portal"); authenticationFilter.doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(response).sendRedirect("/bonita/login.jsp?tenant=12&redirectUrl="); } @Test public void testFilter() throws Exception { when(httpRequest.getSession()).thenReturn(httpSession); doAnswer(new Answer<Object>() { @Override public Object answer(final InvocationOnMock invocation) throws Throwable { return null; } }).when(authenticationFilter).doAuthenticationFiltering(any(HttpServletRequestAccessor.class), any(HttpServletResponse.class), any(TenantIdAccessor.class), any(FilterChain.class)); authenticationFilter.doFilter(httpRequest, httpResponse, chain); verify(authenticationFilter, times(1)).doAuthenticationFiltering(any(HttpServletRequestAccessor.class), any(HttpServletResponse.class), any(TenantIdAccessor.class), any(FilterChain.class)); } @Test public void testFilterWithExcludedURL() throws Exception { final String url = "test"; when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url)); doReturn(true).when(authenticationFilter).matchExcludePatterns(url); authenticationFilter.doFilter(httpRequest, httpResponse, chain); verify(authenticationFilter, times(0)).doAuthenticationFiltering(request, response, tenantIdAccessor, chain); verify(chain, times(1)).doFilter(httpRequest, httpResponse); } @Test public void testMatchExcludePatterns() throws Exception { matchExcludePattern("/login.jsp", true); matchExcludePattern("http://localhost:8080/login.jsp", true); matchExcludePattern("http://localhost:8080/bonita/mobile/login.jsp", true); matchExcludePattern("http://localhost:8080/portal/themeResource", true); matchExcludePattern("http://localhost:8080/portal/themeResource/poutpout", false); matchExcludePattern("http://localhost:8080/loginservice", true); matchExcludePattern("http://localhost:8080/portal/formsService", true); } @Test public void testMakeRedirectUrl() throws Exception { when(request.getRequestedUri()).thenReturn("/portal/homepage"); final RedirectUrl redirectUrl = authenticationFilter.makeRedirectUrl(request); verify(request, times(1)).getRequestedUri(); assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase("/portal/homepage"); } @Test public void testMakeRedirectUrlFromRequestUrl() throws Exception { when(request.getRequestedUri()).thenReturn("portal/homepage"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://127.0.1.1:8888/portal/homepage")); final RedirectUrl redirectUrl = authenticationFilter.makeRedirectUrl(request); verify(request, times(1)).getRequestedUri(); verify(httpRequest, never()).getRequestURI(); assertThat(redirectUrl.getUrl()).isEqualToIgnoringCase("portal/homepage"); } @Test public void testCompileNullPattern() throws Exception { assertThat(authenticationFilter.compilePattern(null)).isNull(); } @Test public void testCompileWrongPattern() throws Exception { assertThat(authenticationFilter.compilePattern("((((")).isNull(); } @Test public void testCompileSimplePattern() throws Exception { final String patternToCompile = "test"; assertThat(authenticationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<Pattern>() { @Override public boolean matches(final Pattern pattern) { return pattern.pattern().equalsIgnoreCase(patternToCompile); } }); } @Test public void testCompileExcludePattern() throws Exception { final String patternToCompile = "^/(bonita/)?((mobile/)?login.jsp$)|(images/)|(redirectCasToCatchHash.jsp)|(loginservice)|(serverAPI)|(/mobile/js/)|(maintenance.jsp$)|(API/platform/)|(platformloginservice$)|(portal/themeResource$)|(portal/scripts)|(/bonita/?$)|(logoutservice)"; assertThat(authenticationFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<Pattern>() { @Override public boolean matches(final Pattern pattern) { return pattern.pattern().equalsIgnoreCase(patternToCompile); } }); } private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) { authenticationFilter.excludePattern = Pattern.compile(excludeAuthenticationPattern); if (authenticationFilter.matchExcludePatterns(urlToMatch) != mustMatch) { Assertions.fail("Matching excludePattern and the Url " + urlToMatch + " must return " + mustMatch); } } private AuthenticationRule createPassingRule() { return new AuthenticationRule() { @Override public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response, final TenantIdAccessor tenantIdAccessor) throws ServletException { return true; } }; } private AuthenticationRule createFailingRule() { return new AuthenticationRule() { @Override public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response, final TenantIdAccessor tenantIdAccessor) throws ServletException { return false; } }; } }