/* * Copyright (C) 2014 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.errai.security.server; import static org.jboss.errai.security.server.FormAuthenticationScheme.*; import static org.junit.Assert.*; import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.picketlink.idm.model.basic.User; public class FormBasedLoginTest extends BaseSecurityFilterTest { /** * Turns the request field into a login request as recognized by our form-based login scheme. * * @param username * the username the auth scheme should see * @param password * the password the auth scheme should see */ private void setRequestAsLogin(String username, String password) { request.setServletPath(HTTP_FORM_SECURITY_CHECK_URI); request.setRequestURI(contextPath + HTTP_FORM_SECURITY_CHECK_URI); request.setParameter(HTTP_FORM_USERNAME_PARAM, username); request.setParameter(HTTP_FORM_PASSWORD_PARAM, password); } /** * The client-side of the security framework watches for 4xx errors on ErraiBus communication attempts, and it does a * redirect to the login page when that happens. This test ensures unauthenticated ErraiBus requests result in a 403 * error. */ @Test public void test403WhenNotAuthenticated() throws Exception { request.setServletPath("/in.erraiBus"); request.setRequestURI("/test-context/in.erraiBus"); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); verify(response).sendError(eq(403)); } @Test public void shouldPassAuthenticatedRequestsThrough() throws Exception { // given: user is logged in identity.setLoggedInUser(new User("previously_logged_in")); request.setServletPath("/in.erraiBus"); request.setRequestURI("/test-context/in.erraiBus"); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); // make sure the filter didn't commit the response, with specific checks for the most likely reasons it might have verify(response, never()).sendError(anyInt(), anyString()); verify(response, never()).sendError(anyInt()); verify(response, never()).sendRedirect(anyString()); assertFalse(response.isCommitted()); // and most importantly, it passed the request up the filter chain! verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } /** * This test protects the redirect-after-login behaviour that the uberfire-tutorial project relies on. */ @Test public void successfulFormBasedLoginShouldRedirectToHostPageUrl() throws Exception { final String hostPageUri = "/MyGwtModule/MyGwtHostPage.html"; filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); setRequestAsLogin("username", "password"); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response, never()).sendError(anyInt()); verify(response, never()).sendError(anyInt(), anyString()); verify(response).sendRedirect(contextPath + hostPageUri); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } @Test public void newLoginAttemptShouldTakePrecedenceOverExistingSessionData() throws Exception { final String hostPageUri = "/MyGwtModule/MyGwtHostPage.html"; filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); // given: user is logged in identity.setLoggedInUser(new User("previously_logged_in")); setRequestAsLogin("logged_in_via_form", "logged_in_via_form"); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); // the new form-based login attempt must take precedence over the existing session info assertEquals("logged_in_via_form", ((User) identity.getAccount()).getLoginName()); // and the host page redirect should have happened too verify(response, never()).sendError(anyInt()); verify(response, never()).sendError(anyInt(), anyString()); verify(response).sendRedirect(contextPath + hostPageUri); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } /** * The request in this test is not a login request; it's for an ongoing session. It targets the configured GWT host * page, which is actually a common scenario. This request must get through the filter without a redirect. */ @Test public void authenticatedRequestToHostPageUrlShouldNotRedirectBackToItself() throws Exception { final String hostPageUri = "/host-page.html"; // given: user is logged in identity.setLoggedInUser(new User("previously_logged_in")); filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); request.setServletPath(""); request.setRequestURI(contextPath + hostPageUri); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); // the host page redirect must not have happened (it would be a loop) verify(response, never()).sendError(anyInt()); verify(response, never()).sendError(anyInt(), anyString()); verify(response, never()).sendRedirect(contextPath + hostPageUri); // redundant but has a better failure message verify(response, never()).sendRedirect(anyString()); verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } /** * The request in this test is not a login request; it's for an ongoing session. It should not get redirected to the * GWT host page. */ @Test public void authenticatedRequestToAnyUrlShouldNotRedirectToHostPageUrl() throws Exception { final String hostPageUri = "/HostPage-uri.html"; // given: user is logged in identity.setLoggedInUser(new User("previously_logged_in")); filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); request.setServletPath(""); request.setRequestURI(contextPath + "/foo.css"); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); // the host page redirect must not have happened (it would be a loop) verify(response, never()).sendError(anyInt()); verify(response, never()).sendError(anyInt(), anyString()); verify(response, never()).sendRedirect(contextPath + hostPageUri); // redundant but has a better failure message verify(response, never()).sendRedirect(anyString()); verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } @Test public void shouldRedirectToLoginPageUponUnauthenticatedRequestToHostPage() throws Exception { final String hostPageUri = "/HostPage-uri.html"; final String loginPageUri = "/login.jsp"; filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); filterConfig.initParams.put(LOGIN_PAGE_INIT_PARAM, loginPageUri); request.setRequestURI(contextPath + hostPageUri); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response).sendRedirect(contextPath + loginPageUri); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } @Test public void shouldRedirectToBackToLoginPageUponFailedLogin() throws Exception { final String loginPageUri = "/login.jsp"; filterConfig.initParams.put(LOGIN_PAGE_INIT_PARAM, loginPageUri); setRequestAsLogin("username", "password"); identity.setAllowsLogins(false); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response).sendRedirect(contextPath + loginPageUri + "?" + LOGIN_ERROR_QUERY_PARAM + "=true"); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); } @Test public void shouldEchoBackQueryParametersOnSuccessfulLogin() throws Exception { final String loginPageUri = "/login.jsp"; filterConfig.initParams.put(LOGIN_PAGE_INIT_PARAM, loginPageUri); setRequestAsLogin("username", "password"); request.setParameter("extra=Param", "extraParam&Value"); request.setParameter("extra?Param2", "extraParam<Value2"); final ArgumentCaptor<String> responseStringCaptor = ArgumentCaptor.forClass(String.class); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response).sendRedirect(responseStringCaptor.capture()); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); assertTrue(responseStringCaptor.getValue().contains("extra%3DParam=extraParam%26Value")); assertTrue(responseStringCaptor.getValue().contains("extra%3FParam2=extraParam%3CValue2")); } @Test public void shouldEchoBackQueryParametersOnFailedLogin() throws Exception { final String loginPageUri = "/login.jsp"; filterConfig.initParams.put(LOGIN_PAGE_INIT_PARAM, loginPageUri); setRequestAsLogin("username", "password"); request.setParameter("extra=Param", "extraParam&Value"); request.setParameter("extra?Param2", "extraParam<Value2"); identity.setAllowsLogins(false); final ArgumentCaptor<String> responseStringCaptor = ArgumentCaptor.forClass(String.class); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response).sendRedirect(responseStringCaptor.capture()); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); assertTrue(responseStringCaptor.getValue().contains("extra%3DParam=extraParam%26Value")); assertTrue(responseStringCaptor.getValue().contains("extra%3FParam2=extraParam%3CValue2")); } @Test public void shouldEchoBackQueryParametersOnRedirectFromHostPageToLoginPage() throws Exception { final String hostPageUri = "/gwt_host_page.html"; final String loginPageUri = "/login.jsp"; filterConfig.initParams.put(HOST_PAGE_INIT_PARAM, hostPageUri); filterConfig.initParams.put(LOGIN_PAGE_INIT_PARAM, loginPageUri); request.setRequestURI(contextPath + hostPageUri); request.setParameter("extra=Param", "extraParam&Value"); request.setParameter("extra?Param2", "extraParam<Value2"); final ArgumentCaptor<String> responseStringCaptor = ArgumentCaptor.forClass(String.class); authFilter.init(filterConfig); authFilter.doFilter(request, response, filterChain); verify(response).sendRedirect(responseStringCaptor.capture()); verify(filterChain, never()).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); assertTrue(responseStringCaptor.getValue().contains("extra%3DParam=extraParam%26Value")); assertTrue(responseStringCaptor.getValue().contains("extra%3FParam2=extraParam%3CValue2")); // this is not a login error, so ensure the error param is not present assertFalse(responseStringCaptor.getValue().contains(LOGIN_ERROR_QUERY_PARAM)); } }