diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-09-29 16:44:41 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-10-17 17:08:53 +0200 |
commit | 33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc (patch) | |
tree | ab89a2c4a5e0692cb233a18f36ed21933c0b122d /server | |
parent | 6ddae4222b30ee89c15f1090ab2ac5fb6dc11a16 (diff) | |
download | sonarqube-33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc.tar.gz sonarqube-33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc.zip |
SONAR-7590 Redirect to requested page with identity provider
Diffstat (limited to 'server')
10 files changed, 312 insertions, 10 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java index e905fe87ed4..b84b1abb32a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java @@ -43,6 +43,7 @@ public class AuthenticationModule extends Module { JwtSerializer.class, JwtHttpHandler.class, JwtCsrfVerifier.class, + OAuth2Redirection.class, LoginAction.class, LogoutAction.class, CredentialsAuthenticator.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java index ed9e7b27ea6..5bd51538237 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java @@ -47,13 +47,15 @@ public class InitFilter extends AuthenticationFilter { private final BaseContextFactory baseContextFactory; private final OAuth2ContextFactory oAuth2ContextFactory; private final AuthenticationEvent authenticationEvent; + private final OAuth2Redirection oAuthRedirection; public InitFilter(IdentityProviderRepository identityProviderRepository, BaseContextFactory baseContextFactory, - OAuth2ContextFactory oAuth2ContextFactory, Server server, AuthenticationEvent authenticationEvent) { + OAuth2ContextFactory oAuth2ContextFactory, Server server, AuthenticationEvent authenticationEvent, OAuth2Redirection oAuthRedirection) { super(server, identityProviderRepository); this.baseContextFactory = baseContextFactory; this.oAuth2ContextFactory = oAuth2ContextFactory; this.authenticationEvent = authenticationEvent; + this.oAuthRedirection = oAuthRedirection; } @Override @@ -82,9 +84,11 @@ public class InitFilter extends AuthenticationFilter { handleError(response, format("Unsupported IdentityProvider class: %s", provider.getClass())); } } catch (AuthenticationException e) { + oAuthRedirection.delete(request, response); authenticationEvent.loginFailure(request, e); handleAuthenticationError(e, response, getContextPath()); } catch (Exception e) { + oAuthRedirection.delete(request, response); handleError(e, response, format("Fail to initialize authentication with provider '%s'", provider.getKey())); } } @@ -103,6 +107,7 @@ public class InitFilter extends AuthenticationFilter { private void handleOAuth2IdentityProvider(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider provider) { try { + oAuthRedirection.create(request, response); provider.init(oAuth2ContextFactory.newContext(request, response, provider)); } catch (UnauthorizedException e) { throw AuthenticationException.newBuilder() diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java index 31b2b6543dc..63e9853865a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java @@ -45,12 +45,14 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { private final OAuth2ContextFactory oAuth2ContextFactory; private final AuthenticationEvent authenticationEvent; + private final OAuth2Redirection oAuthRedirection; public OAuth2CallbackFilter(IdentityProviderRepository identityProviderRepository, OAuth2ContextFactory oAuth2ContextFactory, - Server server, AuthenticationEvent authenticationEvent) { + Server server, AuthenticationEvent authenticationEvent, OAuth2Redirection oAuthRedirection) { super(server, identityProviderRepository); this.oAuth2ContextFactory = oAuth2ContextFactory; this.authenticationEvent = authenticationEvent; + this.oAuthRedirection = oAuthRedirection; } @Override @@ -77,9 +79,11 @@ public class OAuth2CallbackFilter extends AuthenticationFilter { handleError(response, format("Not an OAuth2IdentityProvider: %s", provider.getClass())); } } catch (AuthenticationException e) { + oAuthRedirection.delete(request, response); authenticationEvent.loginFailure(request, e); handleAuthenticationError(e, response, getContextPath()); } catch (Exception e) { + oAuthRedirection.delete(request, response); handleError(e, response, format("Fail to callback authentication with '%s'", provider.getKey())); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java index d19d8a1fd2e..ac051e4ab4a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java @@ -20,6 +20,7 @@ package org.sonar.server.authentication; import java.io.IOException; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.sonar.api.platform.Server; @@ -43,15 +44,17 @@ public class OAuth2ContextFactory { private final OAuthCsrfVerifier csrfVerifier; private final JwtHttpHandler jwtHttpHandler; private final UserSessionFactory userSessionFactory; + private final OAuth2Redirection oAuthRedirection; public OAuth2ContextFactory(ThreadLocalUserSession threadLocalUserSession, UserIdentityAuthenticator userIdentityAuthenticator, Server server, - OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler, UserSessionFactory userSessionFactory) { + OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler, UserSessionFactory userSessionFactory, OAuth2Redirection oAuthRedirection) { this.threadLocalUserSession = threadLocalUserSession; this.userIdentityAuthenticator = userIdentityAuthenticator; this.server = server; this.csrfVerifier = csrfVerifier; this.jwtHttpHandler = jwtHttpHandler; this.userSessionFactory = userSessionFactory; + this.oAuthRedirection = oAuthRedirection; } public OAuth2IdentityProvider.InitContext newContext(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider identityProvider) { @@ -111,7 +114,8 @@ public class OAuth2ContextFactory { @Override public void redirectToRequestedPage() { try { - getResponse().sendRedirect(server.getContextPath() + "/"); + Optional<String> redirectTo = oAuthRedirection.getAndDelete(request, response); + getResponse().sendRedirect(server.getContextPath() + redirectTo.orElse("/")); } catch (IOException e) { throw new IllegalStateException("Fail to redirect to home", e); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2Redirection.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2Redirection.java new file mode 100644 index 00000000000..c66d54e7ae5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2Redirection.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.authentication; + +import java.util.Optional; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.server.authentication.Cookies.findCookie; +import static org.sonar.server.authentication.Cookies.newCookieBuilder; + +public class OAuth2Redirection { + + private static final String REDIRECT_TO_COOKIE = "REDIRECT_TO"; + private static final String RETURN_TO_PARAMETER = "return_to"; + + public void create(HttpServletRequest request, HttpServletResponse response) { + String redirectTo = request.getParameter(RETURN_TO_PARAMETER); + if (isBlank(redirectTo)) { + return; + } + response.addCookie(newCookieBuilder(request) + .setName(REDIRECT_TO_COOKIE) + .setValue(redirectTo) + .setHttpOnly(true) + .setExpiry(-1) + .build()); + } + + public Optional<String> getAndDelete(HttpServletRequest request, HttpServletResponse response) { + Optional<Cookie> cookie = findCookie(REDIRECT_TO_COOKIE, request); + if (!cookie.isPresent()) { + return Optional.empty(); + } + + delete(request, response); + + String redirectTo = cookie.get().getValue(); + if (isBlank(redirectTo)) { + return Optional.empty(); + } + return Optional.of(redirectTo); + } + + public void delete(HttpServletRequest request, HttpServletResponse response) { + response.addCookie(newCookieBuilder(request) + .setName(REDIRECT_TO_COOKIE) + .setValue(null) + .setHttpOnly(true) + .setExpiry(0) + .build()); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java index 75e5a123ea7..47e781e7969 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java @@ -30,7 +30,7 @@ public class AuthenticationModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new AuthenticationModule().configure(container); - assertThat(container.size()).isEqualTo(2 + 21); + assertThat(container.size()).isEqualTo(2 + 22); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java index 17bd5090c33..02fc9c42f57 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java @@ -72,10 +72,11 @@ public class InitFilterTest { private FakeBasicIdentityProvider baseIdentityProvider = new FakeBasicIdentityProvider(BASIC_PROVIDER_KEY, true); private BaseIdentityProvider.Context baseContext = mock(BaseIdentityProvider.Context.class); private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); + private OAuth2Redirection oAuthRedirection = mock(OAuth2Redirection.class); private ArgumentCaptor<AuthenticationException> authenticationExceptionCaptor = ArgumentCaptor.forClass(AuthenticationException.class); - private InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory, server, authenticationEvent); + private InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory, server, authenticationEvent, oAuthRedirection); @Before public void setUp() throws Exception { @@ -99,6 +100,7 @@ public class InitFilterTest { assertOAuth2InitCalled(); verifyZeroInteractions(authenticationEvent); + verify(oAuthRedirection).create(eq(request), eq(response)); } @Test @@ -110,6 +112,7 @@ public class InitFilterTest { assertOAuth2InitCalled(); verifyZeroInteractions(authenticationEvent); + verify(oAuthRedirection).create(eq(request), eq(response)); } @Test @@ -121,6 +124,7 @@ public class InitFilterTest { assertBasicInitCalled(); verifyZeroInteractions(authenticationEvent); + verifyZeroInteractions(oAuthRedirection); } @Test @@ -131,6 +135,7 @@ public class InitFilterTest { assertError("No provider key found in URI"); verifyZeroInteractions(authenticationEvent); + verifyZeroInteractions(oAuthRedirection); } @Test @@ -141,6 +146,7 @@ public class InitFilterTest { assertError("No provider key found in URI"); verifyZeroInteractions(authenticationEvent); + verifyZeroInteractions(oAuthRedirection); } @Test @@ -154,6 +160,7 @@ public class InitFilterTest { assertError("Unsupported IdentityProvider class: class org.sonar.server.authentication.InitFilterTest$UnsupportedIdentityProvider"); verifyZeroInteractions(authenticationEvent); + verifyZeroInteractions(oAuthRedirection); } @Test @@ -171,6 +178,7 @@ public class InitFilterTest { assertThat(authenticationException.getSource()).isEqualTo(AuthenticationEvent.Source.external(identityProvider)); assertThat(authenticationException.getLogin()).isNull(); assertThat(authenticationException.getPublicMessage()).isEqualTo("Email john@email.com is already used"); + verifyDeleteRedirection(); } @Test @@ -183,6 +191,20 @@ public class InitFilterTest { underTest.doFilter(request, response, chain); verify(response).sendRedirect("/sonarqube/sessions/unauthorized?message=Email+john%40email.com+is+already+used"); + verifyDeleteRedirection(); + } + + @Test + public void redirect_when_failing_because_of_Exception() throws Exception { + IdentityProvider identityProvider = new FailWithIllegalStateException("failing"); + when(request.getRequestURI()).thenReturn("/sessions/init/" + identityProvider.getKey()); + identityProviderRepository.addIdentityProvider(identityProvider); + + underTest.doFilter(request, response, chain); + + verify(response).sendRedirect("/sessions/unauthorized"); + assertThat(logTester.logs(LoggerLevel.ERROR)).containsExactlyInAnyOrder("Fail to initialize authentication with provider 'failing'"); + verifyDeleteRedirection(); } private void assertOAuth2InitCalled() { @@ -201,6 +223,10 @@ public class InitFilterTest { assertThat(oAuth2IdentityProvider.isInitCalled()).isFalse(); } + private void verifyDeleteRedirection() { + verify(oAuthRedirection).delete(eq(request), eq(response)); + } + private static class FailWithUnauthorizedExceptionIdProvider extends FakeBasicIdentityProvider { public FailWithUnauthorizedExceptionIdProvider(String key) { @@ -213,6 +239,18 @@ public class InitFilterTest { } } + private static class FailWithIllegalStateException extends FakeBasicIdentityProvider { + + public FailWithIllegalStateException(String key) { + super(key, true); + } + + @Override + public void init(Context context) { + throw new IllegalStateException("Failure !"); + } + } + private static class UnsupportedIdentityProvider implements IdentityProvider { private final String unsupportedKey; @@ -239,6 +277,7 @@ public class InitFilterTest { public boolean isEnabled() { return true; } + @Override public boolean allowsUsersToSignUp() { return false; diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java index 6d2f7857840..9cb20e6de80 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java @@ -65,10 +65,11 @@ public class OAuth2CallbackFilterTest { private FakeOAuth2IdentityProvider oAuth2IdentityProvider = new WellbehaveFakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true, LOGIN); private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class); + private OAuth2Redirection oAuthRedirection = mock(OAuth2Redirection.class); private ArgumentCaptor<AuthenticationException> authenticationExceptionCaptor = ArgumentCaptor.forClass(AuthenticationException.class); - private OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory, server, authenticationEvent); + private OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory, server, authenticationEvent, oAuthRedirection); @Before public void setUp() throws Exception { @@ -164,6 +165,7 @@ public class OAuth2CallbackFilterTest { assertThat(authenticationException.getSource()).isEqualTo(Source.oauth2(identityProvider)); assertThat(authenticationException.getLogin()).isNull(); assertThat(authenticationException.getPublicMessage()).isEqualTo("Email john@email.com is already used"); + verify(oAuthRedirection).delete(eq(request), eq(response)); } @Test @@ -180,6 +182,24 @@ public class OAuth2CallbackFilterTest { underTest.doFilter(request, response, chain); verify(response).sendRedirect("/sonarqube/sessions/unauthorized?message=Email+john%40email.com+is+already+used"); + verify(oAuthRedirection).delete(eq(request), eq(response)); + } + + @Test + public void redirect_when_failing_because_of_Exception() throws Exception { + FailWithIllegalStateException identityProvider = new FailWithIllegalStateException(); + identityProvider + .setKey("failing") + .setName("name of failing") + .setEnabled(true); + when(request.getRequestURI()).thenReturn("/oauth2/callback/" + identityProvider.getKey()); + identityProviderRepository.addIdentityProvider(identityProvider); + + underTest.doFilter(request, response, chain); + + verify(response).sendRedirect("/sessions/unauthorized"); + assertThat(logTester.logs(LoggerLevel.ERROR)).containsExactlyInAnyOrder("Fail to callback authentication with 'failing'"); + verify(oAuthRedirection).delete(eq(request), eq(response)); } @Test @@ -216,6 +236,19 @@ public class OAuth2CallbackFilterTest { } } + private static class FailWithIllegalStateException extends TestIdentityProvider implements OAuth2IdentityProvider { + + @Override + public void init(InitContext context) { + + } + + @Override + public void callback(CallbackContext context) { + throw new IllegalStateException("Failure !"); + } + } + /** * An extension of {@link FakeOAuth2IdentityProvider} that actually call {@link org.sonar.api.server.authentication.OAuth2IdentityProvider.CallbackContext#authenticate(UserIdentity)}. */ diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java index 16f0c16d6d4..90322fbfb50 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.authentication; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -73,12 +74,14 @@ public class OAuth2ContextFactoryTest { private OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class); private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class); private TestUserSessionFactory userSessionFactory = TestUserSessionFactory.standalone(); + private OAuth2Redirection oAuthRedirection = mock(OAuth2Redirection.class); private HttpServletRequest request = mock(HttpServletRequest.class); private HttpServletResponse response = mock(HttpServletResponse.class); private HttpSession session = mock(HttpSession.class); private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class); - private OAuth2ContextFactory underTest = new OAuth2ContextFactory(threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler, userSessionFactory); + private OAuth2ContextFactory underTest = new OAuth2ContextFactory(threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler, userSessionFactory, + oAuthRedirection); @Before public void setUp() throws Exception { @@ -143,8 +146,9 @@ public class OAuth2ContextFactoryTest { } @Test - public void redirect_to_requested_page() throws Exception { + public void redirect_to_home() throws Exception { when(server.getContextPath()).thenReturn(""); + when(oAuthRedirection.getAndDelete(request, response)).thenReturn(Optional.empty()); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); callback.redirectToRequestedPage(); @@ -153,8 +157,9 @@ public class OAuth2ContextFactoryTest { } @Test - public void redirect_to_requested_page_with_context() throws Exception { + public void redirect_to_home_with_context() throws Exception { when(server.getContextPath()).thenReturn("/sonarqube"); + when(oAuthRedirection.getAndDelete(request, response)).thenReturn(Optional.empty()); OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); callback.redirectToRequestedPage(); @@ -163,6 +168,28 @@ public class OAuth2ContextFactoryTest { } @Test + public void redirect_to_requested_page() throws Exception { + when(oAuthRedirection.getAndDelete(request, response)).thenReturn(Optional.of("/settings")); + when(server.getContextPath()).thenReturn(""); + OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); + + callback.redirectToRequestedPage(); + + verify(response).sendRedirect("/settings"); + } + + @Test + public void redirect_to_requested_page_context() throws Exception { + when(oAuthRedirection.getAndDelete(request, response)).thenReturn(Optional.of("/settings")); + when(server.getContextPath()).thenReturn("/sonarqube"); + OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); + + callback.redirectToRequestedPage(); + + verify(response).sendRedirect("/sonarqube/settings"); + } + + @Test public void verify_csrf_state() throws Exception { OAuth2IdentityProvider.CallbackContext callback = newCallbackContext(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2RedirectionTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2RedirectionTest.java new file mode 100644 index 00000000000..e25bfb2deeb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2RedirectionTest.java @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.authentication; + +import java.util.Optional; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.api.platform.Server; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OAuth2RedirectionTest { + + private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class); + + private Server server = mock(Server.class); + private HttpServletResponse response = mock(HttpServletResponse.class); + private HttpServletRequest request = mock(HttpServletRequest.class); + + private OAuth2Redirection underTest = new OAuth2Redirection(); + + @Before + public void setUp() throws Exception { + when(server.getContextPath()).thenReturn(""); + } + + @Test + public void create_cookie() throws Exception { + when(request.getParameter("return_to")).thenReturn("/settings"); + + underTest.create(request, response); + + verify(response).addCookie(cookieArgumentCaptor.capture()); + Cookie cookie = cookieArgumentCaptor.getValue(); + assertThat(cookie.getName()).isEqualTo("REDIRECT_TO"); + assertThat(cookie.getValue()).isEqualTo("/settings"); + assertThat(cookie.getPath()).isEqualTo("/"); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(-1); + assertThat(cookie.getSecure()).isFalse(); + } + + @Test + public void does_not_create_cookie_when_return_to_parameter_is_empty() { + when(request.getParameter("return_to")).thenReturn(""); + + underTest.create(request, response); + + verify(response, never()).addCookie(any()); + } + + @Test + public void does_not_create_cookie_when_return_to_parameter_is_null() { + when(request.getParameter("return_to")).thenReturn(null); + + underTest.create(request, response); + + verify(response, never()).addCookie(any()); + } + + @Test + public void get_and_delete() throws Exception { + when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("REDIRECT_TO", "/settings")}); + + Optional<String> redirection = underTest.getAndDelete(request, response); + + assertThat(redirection).isEqualTo(Optional.of("/settings")); + verify(response).addCookie(cookieArgumentCaptor.capture()); + Cookie updatedCookie = cookieArgumentCaptor.getValue(); + assertThat(updatedCookie.getName()).isEqualTo("REDIRECT_TO"); + assertThat(updatedCookie.getValue()).isNull(); + assertThat(updatedCookie.getPath()).isEqualTo("/"); + assertThat(updatedCookie.getMaxAge()).isEqualTo(0); + } + + @Test + public void delete() throws Exception { + when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("REDIRECT_TO", "/settings")}); + + underTest.delete(request, response); + + verify(response).addCookie(cookieArgumentCaptor.capture()); + Cookie updatedCookie = cookieArgumentCaptor.getValue(); + assertThat(updatedCookie.getName()).isEqualTo("REDIRECT_TO"); + assertThat(updatedCookie.getValue()).isNull(); + assertThat(updatedCookie.getPath()).isEqualTo("/"); + assertThat(updatedCookie.getMaxAge()).isEqualTo(0); + } + +} |