aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-09-29 16:44:41 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2017-10-17 17:08:53 +0200
commit33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc (patch)
treeab89a2c4a5e0692cb233a18f36ed21933c0b122d /server
parent6ddae4222b30ee89c15f1090ab2ac5fb6dc11a16 (diff)
downloadsonarqube-33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc.tar.gz
sonarqube-33eb0b2b67bcbfbbbf98fca52480b735d3b18fbc.zip
SONAR-7590 Redirect to requested page with identity provider
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2Redirection.java73
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java41
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java35
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java33
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2RedirectionTest.java116
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);
+ }
+
+}