aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2019-06-21 18:20:16 +0200
committersonartech <sonartech@sonarsource.com>2019-06-28 08:45:54 +0200
commit9669c6d690b5ced80d0b9b80feeb8b9813284ebe (patch)
tree33ab6cd647849bd68a03e864124db4ee4c87a8b4 /server
parentf535399ec7ccf1807d3866cfb17cce3571ca38b5 (diff)
downloadsonarqube-9669c6d690b5ced80d0b9b80feeb8b9813284ebe.tar.gz
sonarqube-9669c6d690b5ced80d0b9b80feeb8b9813284ebe.zip
SONAR-12148 Generate cookie in case of authentication error or email update
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationError.java41
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java16
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/InitFilter.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2CallbackFilter.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java33
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/InitFilterTest.java54
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2CallbackFilterTest.java50
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java15
13 files changed, 183 insertions, 96 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationError.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationError.java
index 7e295b31680..b0a79c33687 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationError.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationError.java
@@ -19,49 +19,56 @@
*/
package org.sonar.server.authentication;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.server.authentication.event.AuthenticationException;
-import static java.lang.String.format;
import static org.sonar.server.authentication.AuthenticationRedirection.encodeMessage;
import static org.sonar.server.authentication.AuthenticationRedirection.redirectTo;
+import static org.sonar.server.authentication.Cookies.newCookieBuilder;
-final class AuthenticationError {
+public final class AuthenticationError {
private static final String UNAUTHORIZED_PATH = "/sessions/unauthorized";
- private static final String UNAUTHORIZED_PATH_WITH_MESSAGE = UNAUTHORIZED_PATH + "?message=%s";
+
private static final Logger LOGGER = Loggers.get(AuthenticationError.class);
+ private static final String AUTHENTICATION_ERROR_COOKIE = "AUTHENTICATION-ERROR";
+ private static final int FIVE_MINUTES_IN_SECONDS = 5 * 60;
private AuthenticationError() {
// Utility class
}
- static void handleError(Exception e, HttpServletResponse response, String message) {
+ static void handleError(Exception e, HttpServletRequest request, HttpServletResponse response, String message) {
LOGGER.warn(message, e);
- redirectToUnauthorized(response);
+ redirectToUnauthorized(request, response);
}
- static void handleError(HttpServletResponse response, String message) {
+ static void handleError(HttpServletRequest request, HttpServletResponse response, String message) {
LOGGER.warn(message);
- redirectToUnauthorized(response);
- }
-
- static void handleAuthenticationError(AuthenticationException e, HttpServletResponse response, String contextPath) {
- redirectTo(response, getPath(e, contextPath));
+ redirectToUnauthorized(request, response);
}
- private static String getPath(AuthenticationException e, String contextPath) {
+ static void handleAuthenticationError(AuthenticationException e, HttpServletRequest request, HttpServletResponse response) {
String publicMessage = e.getPublicMessage();
- if (publicMessage == null || publicMessage.isEmpty()) {
- return UNAUTHORIZED_PATH;
+ if (publicMessage != null && !publicMessage.isEmpty()) {
+ addErrorCookie(request, response, publicMessage);
}
- return contextPath + format(UNAUTHORIZED_PATH_WITH_MESSAGE, encodeMessage(publicMessage));
+ redirectToUnauthorized(request, response);
}
- public static void redirectToUnauthorized(HttpServletResponse response) {
- redirectTo(response, UNAUTHORIZED_PATH);
+ public static void addErrorCookie(HttpServletRequest request, HttpServletResponse response, String value) {
+ response.addCookie(newCookieBuilder(request)
+ .setName(AUTHENTICATION_ERROR_COOKIE)
+ .setValue(encodeMessage(value))
+ .setHttpOnly(false)
+ .setExpiry(FIVE_MINUTES_IN_SECONDS)
+ .build());
}
+ private static void redirectToUnauthorized(HttpServletRequest request, HttpServletResponse response) {
+ redirectTo(response, request.getContextPath() + UNAUTHORIZED_PATH);
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java
index b77d5cc2d21..244ccfbc2a8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationFilter.java
@@ -22,7 +22,6 @@ package org.sonar.server.authentication;
import javax.annotation.CheckForNull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.web.ServletFilter;
@@ -31,12 +30,11 @@ import static java.lang.String.format;
import static org.sonar.server.authentication.AuthenticationError.handleError;
public abstract class AuthenticationFilter extends ServletFilter {
+
static final String CALLBACK_PATH = "/oauth2/callback/";
private final IdentityProviderRepository identityProviderRepository;
- private final Server server;
- public AuthenticationFilter(Server server, IdentityProviderRepository identityProviderRepository) {
- this.server = server;
+ public AuthenticationFilter(IdentityProviderRepository identityProviderRepository) {
this.identityProviderRepository = identityProviderRepository;
}
@@ -47,15 +45,15 @@ public abstract class AuthenticationFilter extends ServletFilter {
@CheckForNull
IdentityProvider resolveProviderOrHandleResponse(HttpServletRequest request, HttpServletResponse response, String path) {
String requestUri = request.getRequestURI();
- String providerKey = extractKeyProvider(requestUri, server.getContextPath() + path);
+ String providerKey = extractKeyProvider(requestUri, request.getContextPath() + path);
if (providerKey == null) {
- handleError(response, "No provider key found in URI");
+ handleError(request, response, "No provider key found in URI");
return null;
}
try {
return identityProviderRepository.getEnabledByKey(providerKey);
} catch (Exception e) {
- handleError(e, response, format("Failed to retrieve IdentityProvider for key '%s'", providerKey));
+ handleError(e, request, response, format("Failed to retrieve IdentityProvider for key '%s'", providerKey));
return null;
}
}
@@ -70,8 +68,4 @@ public abstract class AuthenticationFilter extends ServletFilter {
}
return null;
}
-
- String getContextPath() {
- return server.getContextPath();
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java
index 191e664691d..b3e91772dd1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationRedirection.java
@@ -35,7 +35,9 @@ public class AuthenticationRedirection {
public static String encodeMessage(String message) {
try {
- return encode(message, UTF_8.name());
+ return encode(message, UTF_8.name())
+ // In order for Javascript to be able to decode this message, + must be replaced by %20
+ .replace("+", "%20");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(format("Fail to encode %s", message), e);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java
index 3ac7f29a349..1af5276cbcd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/Cookies.java
@@ -31,7 +31,7 @@ import static java.util.Objects.requireNonNull;
/**
* Helper class to create a {@link javax.servlet.http.Cookie}.
*
- * The {@link javax.servlet.http.Cookie#secure} will automatically be set.
+ * The {@link javax.servlet.http.Cookie#setSecure(boolean)} will automatically be set to true.
*/
public class Cookies {
@@ -65,7 +65,7 @@ public class Cookies {
private boolean httpOnly;
private int expiry;
- public CookieBuilder(HttpServletRequest request) {
+ CookieBuilder(HttpServletRequest request) {
this.request = request;
}
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 33d9ed3300d..180ccd29501 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
@@ -25,14 +25,14 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.RedirectionException;
+import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
+import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
import static java.lang.String.format;
import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
@@ -50,8 +50,8 @@ public class InitFilter extends AuthenticationFilter {
private final OAuth2AuthenticationParameters oAuthOAuth2AuthenticationParameters;
public InitFilter(IdentityProviderRepository identityProviderRepository, BaseContextFactory baseContextFactory,
- OAuth2ContextFactory oAuth2ContextFactory, Server server, AuthenticationEvent authenticationEvent, OAuth2AuthenticationParameters oAuthOAuth2AuthenticationParameters) {
- super(server, identityProviderRepository);
+ OAuth2ContextFactory oAuth2ContextFactory, AuthenticationEvent authenticationEvent, OAuth2AuthenticationParameters oAuthOAuth2AuthenticationParameters) {
+ super(identityProviderRepository);
this.baseContextFactory = baseContextFactory;
this.oAuth2ContextFactory = oAuth2ContextFactory;
this.authenticationEvent = authenticationEvent;
@@ -82,18 +82,22 @@ public class InitFilter extends AuthenticationFilter {
oAuthOAuth2AuthenticationParameters.init(request, response);
handleOAuth2IdentityProvider(request, response, (OAuth2IdentityProvider) provider);
} else {
- handleError(response, format("Unsupported IdentityProvider class: %s", provider.getClass()));
+ handleError(request, response, format("Unsupported IdentityProvider class: %s", provider.getClass()));
}
+ } catch (EmailAlreadyExistsRedirectionException e) {
+ oAuthOAuth2AuthenticationParameters.delete(request, response);
+ e.addCookie(request, response);
+ redirectTo(response, e.getPath(request.getContextPath()));
+ } catch (UpdateLoginRedirectionException e) {
+ oAuthOAuth2AuthenticationParameters.delete(request, response);
+ redirectTo(response, e.getPath(request.getContextPath()));
} catch (AuthenticationException e) {
oAuthOAuth2AuthenticationParameters.delete(request, response);
authenticationEvent.loginFailure(request, e);
- handleAuthenticationError(e, response, getContextPath());
- } catch (RedirectionException e) {
- oAuthOAuth2AuthenticationParameters.delete(request, response);
- redirectTo(response, e.getPath(getContextPath()));
+ handleAuthenticationError(e, request, response);
} catch (Exception e) {
oAuthOAuth2AuthenticationParameters.delete(request, response);
- handleError(e, response, format("Fail to initialize authentication with provider '%s'", provider.getKey()));
+ handleError(e, request, response, format("Fail to initialize authentication with provider '%s'", provider.getKey()));
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java
index 24f29b14533..848dfab0ee0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java
@@ -33,10 +33,10 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static java.net.URLDecoder.decode;
-import static java.net.URLEncoder.encode;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Optional.empty;
import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.sonar.server.authentication.AuthenticationRedirection.encodeMessage;
import static org.sonar.server.authentication.Cookies.findCookie;
import static org.sonar.server.authentication.Cookies.newCookieBuilder;
@@ -131,11 +131,7 @@ public class OAuth2AuthenticationParametersImpl implements OAuth2AuthenticationP
private static String toJson(Map<String, String> map) {
Gson gson = new GsonBuilder().create();
- try {
- return encode(gson.toJson(map), UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- throw new IllegalStateException(e);
- }
+ return encodeMessage(gson.toJson(map));
}
private static Map<String, String> fromJson(String json) {
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 a2376fad354..b1ab26072ab 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
@@ -25,13 +25,13 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.RedirectionException;
+import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
+import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
import org.sonar.server.user.ThreadLocalUserSession;
import static java.lang.String.format;
@@ -48,8 +48,8 @@ public class OAuth2CallbackFilter extends AuthenticationFilter {
private final ThreadLocalUserSession threadLocalUserSession;
public OAuth2CallbackFilter(IdentityProviderRepository identityProviderRepository, OAuth2ContextFactory oAuth2ContextFactory,
- Server server, AuthenticationEvent authenticationEvent, OAuth2AuthenticationParameters oauth2Parameters, ThreadLocalUserSession threadLocalUserSession) {
- super(server, identityProviderRepository);
+ AuthenticationEvent authenticationEvent, OAuth2AuthenticationParameters oauth2Parameters, ThreadLocalUserSession threadLocalUserSession) {
+ super(identityProviderRepository);
this.oAuth2ContextFactory = oAuth2ContextFactory;
this.authenticationEvent = authenticationEvent;
this.oauth2Parameters = oauth2Parameters;
@@ -77,18 +77,22 @@ public class OAuth2CallbackFilter extends AuthenticationFilter {
if (provider instanceof OAuth2IdentityProvider) {
handleOAuth2Provider(response, request, (OAuth2IdentityProvider) provider);
} else {
- handleError(response, format("Not an OAuth2IdentityProvider: %s", provider.getClass()));
+ handleError(request, response, format("Not an OAuth2IdentityProvider: %s", provider.getClass()));
}
+ } catch (EmailAlreadyExistsRedirectionException e) {
+ oauth2Parameters.delete(request, response);
+ e.addCookie(request, response);
+ redirectTo(response, e.getPath(request.getContextPath()));
+ } catch (UpdateLoginRedirectionException e) {
+ oauth2Parameters.delete(request, response);
+ redirectTo(response, e.getPath(request.getContextPath()));
} catch (AuthenticationException e) {
oauth2Parameters.delete(request, response);
authenticationEvent.loginFailure(request, e);
- handleAuthenticationError(e, response, getContextPath());
- } catch (RedirectionException e) {
- oauth2Parameters.delete(request, response);
- redirectTo(response, e.getPath(getContextPath()));
+ handleAuthenticationError(e, request, response);
} catch (Exception e) {
oauth2Parameters.delete(request, response);
- handleError(e, response, format("Fail to callback authentication with '%s'", provider.getKey()));
+ handleError(e, request, response, format("Fail to callback authentication with '%s'", provider.getKey()));
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
index 6b6fa443ff9..7c6bafe1eb3 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
@@ -103,7 +103,7 @@ public class UserSessionInitializer {
}
if (isNotLocalOrJwt(e.getSource())) {
// redirect to Unauthorized error page
- handleAuthenticationError(e, response, request.getContextPath());
+ handleAuthenticationError(e, request, response);
return false;
}
// Web pages should redirect to the index.html file
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java
index 9cc370c368c..c2d103709b4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/exception/EmailAlreadyExistsRedirectionException.java
@@ -19,12 +19,16 @@
*/
package org.sonar.server.authentication.exception;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.db.user.UserDto;
-import static java.lang.String.format;
-import static org.sonar.server.authentication.AuthenticationRedirection.encodeMessage;
+import static org.sonar.server.authentication.AuthenticationError.addErrorCookie;
/**
* This exception is used to redirect the user to a page explaining him that his email is already used by another account,
@@ -32,7 +36,12 @@ import static org.sonar.server.authentication.AuthenticationRedirection.encodeMe
*/
public class EmailAlreadyExistsRedirectionException extends RedirectionException {
- private static final String PATH = "/sessions/email_already_exists?email=%s&login=%s&provider=%s&existingLogin=%s&existingProvider=%s";
+ private static final String PATH = "/sessions/email_already_exists";
+ private static final String EMAIL_FIELD = "email";
+ private static final String LOGIN_FIELD = "login";
+ private static final String PROVIDER_FIELD = "provider";
+ private static final String EXISTING_LOGIN_FIELD = "existingLogin";
+ private static final String EXISTING_PROVIDER_FIELD = "existingProvider";
private final String email;
private final UserDto existingUser;
@@ -46,13 +55,19 @@ public class EmailAlreadyExistsRedirectionException extends RedirectionException
this.provider = provider;
}
+ public void addCookie(HttpServletRequest request, HttpServletResponse response) {
+ Gson gson = new GsonBuilder().create();
+ String message = gson.toJson(ImmutableMap.of(
+ EMAIL_FIELD, email,
+ LOGIN_FIELD, userIdentity.getProviderLogin(),
+ PROVIDER_FIELD, provider.getKey(),
+ EXISTING_LOGIN_FIELD, existingUser.getExternalLogin(),
+ EXISTING_PROVIDER_FIELD, existingUser.getExternalIdentityProvider()));
+ addErrorCookie(request, response, message);
+ }
+
@Override
public String getPath(String contextPath) {
- return contextPath + format(PATH,
- encodeMessage(email),
- encodeMessage(userIdentity.getProviderLogin()),
- encodeMessage(provider.getKey()),
- encodeMessage(existingUser.getExternalLogin()),
- encodeMessage(existingUser.getExternalIdentityProvider()));
+ return contextPath + PATH;
}
}
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 de18a14d194..c23bb1ed59a 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
@@ -20,6 +20,7 @@
package org.sonar.server.authentication;
import javax.servlet.FilterChain;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
@@ -27,7 +28,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.Display;
import org.sonar.api.server.authentication.IdentityProvider;
@@ -65,7 +65,6 @@ public class InitFilterTest {
private BaseContextFactory baseContextFactory = mock(BaseContextFactory.class);
private OAuth2ContextFactory oAuth2ContextFactory = mock(OAuth2ContextFactory.class);
- private Server server = mock(Server.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
private HttpServletResponse response = mock(HttpServletResponse.class);
@@ -80,14 +79,15 @@ public class InitFilterTest {
private OAuth2AuthenticationParameters auth2AuthenticationParameters = mock(OAuth2AuthenticationParameters.class);
private ArgumentCaptor<AuthenticationException> authenticationExceptionCaptor = ArgumentCaptor.forClass(AuthenticationException.class);
+ private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
- private InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory, server, authenticationEvent, auth2AuthenticationParameters);
+ private InitFilter underTest = new InitFilter(identityProviderRepository, baseContextFactory, oAuth2ContextFactory, authenticationEvent, auth2AuthenticationParameters);
@Before
public void setUp() throws Exception {
when(oAuth2ContextFactory.newContext(request, response, oAuth2IdentityProvider)).thenReturn(oauth2Context);
when(baseContextFactory.newContext(request, response, baseIdentityProvider)).thenReturn(baseContext);
- when(server.getContextPath()).thenReturn("");
+ when(request.getContextPath()).thenReturn("");
}
@Test
@@ -97,7 +97,7 @@ public class InitFilterTest {
@Test
public void do_filter_with_context() {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
when(request.getRequestURI()).thenReturn("/sonarqube/sessions/init/" + OAUTH2_PROVIDER_KEY);
identityProviderRepository.addIdentityProvider(oAuth2IdentityProvider);
@@ -131,7 +131,7 @@ public class InitFilterTest {
@Test
public void init_authentication_parameter_on_auth2_identity_provider() {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
when(request.getRequestURI()).thenReturn("/sonarqube/sessions/init/" + OAUTH2_PROVIDER_KEY);
identityProviderRepository.addIdentityProvider(oAuth2IdentityProvider);
@@ -187,14 +187,14 @@ public class InitFilterTest {
}
@Test
- public void redirect_when_failing_because_of_UnauthorizedExceptionException() throws Exception {
+ public void redirect_contains_cookie_with_error_message_when_failing_because_of_UnauthorizedExceptionException() throws Exception {
IdentityProvider identityProvider = new FailWithUnauthorizedExceptionIdProvider("failing");
when(request.getRequestURI()).thenReturn("/sessions/init/" + identityProvider.getKey());
identityProviderRepository.addIdentityProvider(identityProvider);
underTest.doFilter(request, response, chain);
- verify(response).sendRedirect("/sessions/unauthorized?message=Email+john%40email.com+is+already+used");
+ verify(response).sendRedirect("/sessions/unauthorized");
verify(authenticationEvent).loginFailure(eq(request), authenticationExceptionCaptor.capture());
AuthenticationException authenticationException = authenticationExceptionCaptor.getValue();
assertThat(authenticationException).hasMessage("Email john@email.com is already used");
@@ -202,23 +202,31 @@ public class InitFilterTest {
assertThat(authenticationException.getLogin()).isNull();
assertThat(authenticationException.getPublicMessage()).isEqualTo("Email john@email.com is already used");
verifyDeleteAuthCookie();
+
+ verify(response).addCookie(cookieArgumentCaptor.capture());
+ Cookie cookie = cookieArgumentCaptor.getValue();
+ assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
+ assertThat(cookie.getValue()).isEqualTo("Email%20john%40email.com%20is%20already%20used");
+ assertThat(cookie.getPath()).isEqualTo("/");
+ assertThat(cookie.isHttpOnly()).isFalse();
+ assertThat(cookie.getMaxAge()).isEqualTo(300);
+ assertThat(cookie.getSecure()).isFalse();
}
@Test
public void redirect_with_context_path_when_failing_because_of_UnauthorizedException() throws Exception {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
IdentityProvider identityProvider = new FailWithUnauthorizedExceptionIdProvider("failing");
when(request.getRequestURI()).thenReturn("/sonarqube/sessions/init/" + identityProvider.getKey());
identityProviderRepository.addIdentityProvider(identityProvider);
underTest.doFilter(request, response, chain);
- verify(response).sendRedirect("/sonarqube/sessions/unauthorized?message=Email+john%40email.com+is+already+used");
- verifyDeleteAuthCookie();
+ verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
}
@Test
- public void redirect_when_failing_because_of_EmailAlreadyExistException() throws Exception {
+ public void redirect_contains_cookie_when_failing_because_of_EmailAlreadyExistException() throws Exception {
UserDto existingUser = newUserDto().setEmail("john@email.com").setExternalLogin("john.bitbucket").setExternalIdentityProvider("bitbucket");
FailWithEmailAlreadyExistException identityProvider = new FailWithEmailAlreadyExistException("failing", existingUser);
when(request.getRequestURI()).thenReturn("/sessions/init/" + identityProvider.getKey());
@@ -226,8 +234,16 @@ public class InitFilterTest {
underTest.doFilter(request, response, chain);
- verify(response).sendRedirect("/sessions/email_already_exists?email=john%40email.com&login=john.github&provider=failing&existingLogin=john.bitbucket&existingProvider=bitbucket");
+ verify(response).sendRedirect("/sessions/email_already_exists");
verify(auth2AuthenticationParameters).delete(eq(request), eq(response));
+ verify(response).addCookie(cookieArgumentCaptor.capture());
+ Cookie cookie = cookieArgumentCaptor.getValue();
+ assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
+ assertThat(cookie.getValue()).contains("john%40email.com");
+ assertThat(cookie.getPath()).isEqualTo("/");
+ assertThat(cookie.isHttpOnly()).isFalse();
+ assertThat(cookie.getMaxAge()).isEqualTo(300);
+ assertThat(cookie.getSecure()).isFalse();
}
@Test
@@ -243,6 +259,18 @@ public class InitFilterTest {
verifyDeleteAuthCookie();
}
+ @Test
+ public void redirect_with_context_when_failing_because_of_Exception() throws Exception {
+ when(request.getContextPath()).thenReturn("/sonarqube");
+ IdentityProvider identityProvider = new FailWithIllegalStateException("failing");
+ when(request.getRequestURI()).thenReturn("/sessions/init/" + identityProvider.getKey());
+ identityProviderRepository.addIdentityProvider(identityProvider);
+
+ underTest.doFilter(request, response, chain);
+
+ verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
+ }
+
private void assertOAuth2InitCalled() {
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
assertThat(oAuth2IdentityProvider.isInitCalled()).isTrue();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java
index 1633de1dcd4..f60db60416e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java
@@ -26,7 +26,6 @@ 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.ArgumentMatchers.any;
@@ -40,7 +39,6 @@ public class OAuth2AuthenticationParametersImplTest {
private static final String AUTHENTICATION_COOKIE_NAME = "AUTH-PARAMS";
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);
@@ -48,7 +46,7 @@ public class OAuth2AuthenticationParametersImplTest {
@Before
public void setUp() throws Exception {
- when(server.getContextPath()).thenReturn("");
+ when(request.getContextPath()).thenReturn("");
}
@Test
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 b4a16191590..a42f29cf059 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
@@ -20,6 +20,7 @@
package org.sonar.server.authentication;
import javax.servlet.FilterChain;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
@@ -27,7 +28,6 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
-import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.api.server.authentication.UserIdentity;
@@ -64,7 +64,6 @@ public class OAuth2CallbackFilterTest {
private HttpServletRequest request = mock(HttpServletRequest.class);
private HttpServletResponse response = mock(HttpServletResponse.class);
- private Server server = mock(Server.class);
private FilterChain chain = mock(FilterChain.class);
private FakeOAuth2IdentityProvider oAuth2IdentityProvider = new WellbehaveFakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true, LOGIN);
@@ -73,14 +72,15 @@ public class OAuth2CallbackFilterTest {
private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
private ArgumentCaptor<AuthenticationException> authenticationExceptionCaptor = ArgumentCaptor.forClass(AuthenticationException.class);
+ private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
- private OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory, server, authenticationEvent, oAuthRedirection,
+ private OAuth2CallbackFilter underTest = new OAuth2CallbackFilter(identityProviderRepository, oAuth2ContextFactory, authenticationEvent, oAuthRedirection,
threadLocalUserSession);
@Before
public void setUp() throws Exception {
when(oAuth2ContextFactory.newCallback(request, response, oAuth2IdentityProvider)).thenReturn(mock(OAuth2IdentityProvider.CallbackContext.class));
- when(server.getContextPath()).thenReturn("");
+ when(request.getContextPath()).thenReturn("");
}
@Test
@@ -90,7 +90,7 @@ public class OAuth2CallbackFilterTest {
@Test
public void do_filter_with_context() {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
when(request.getRequestURI()).thenReturn("/sonarqube/oauth2/callback/" + OAUTH2_PROVIDER_KEY);
identityProviderRepository.addIdentityProvider(oAuth2IdentityProvider);
when(threadLocalUserSession.hasSession()).thenReturn(true);
@@ -104,7 +104,7 @@ public class OAuth2CallbackFilterTest {
@Test
public void do_filter_with_context_no_log_if_provider_did_not_call_authenticate_on_context() {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
when(request.getRequestURI()).thenReturn("/sonarqube/oauth2/callback/" + OAUTH2_PROVIDER_KEY);
FakeOAuth2IdentityProvider identityProvider = new FakeOAuth2IdentityProvider(OAUTH2_PROVIDER_KEY, true);
identityProviderRepository.addIdentityProvider(identityProvider);
@@ -164,7 +164,7 @@ public class OAuth2CallbackFilterTest {
underTest.doFilter(request, response, chain);
- verify(response).sendRedirect("/sessions/unauthorized?message=Email+john%40email.com+is+already+used");
+ verify(response).sendRedirect("/sessions/unauthorized");
verify(authenticationEvent).loginFailure(eq(request), authenticationExceptionCaptor.capture());
AuthenticationException authenticationException = authenticationExceptionCaptor.getValue();
assertThat(authenticationException).hasMessage("Email john@email.com is already used");
@@ -172,18 +172,27 @@ public class OAuth2CallbackFilterTest {
assertThat(authenticationException.getLogin()).isNull();
assertThat(authenticationException.getPublicMessage()).isEqualTo("Email john@email.com is already used");
verify(oAuthRedirection).delete(eq(request), eq(response));
+
+ verify(response).addCookie(cookieArgumentCaptor.capture());
+ Cookie cookie = cookieArgumentCaptor.getValue();
+ assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
+ assertThat(cookie.getValue()).isEqualTo("Email%20john%40email.com%20is%20already%20used");
+ assertThat(cookie.getPath()).isEqualTo("/");
+ assertThat(cookie.isHttpOnly()).isFalse();
+ assertThat(cookie.getMaxAge()).isEqualTo(300);
+ assertThat(cookie.getSecure()).isFalse();
}
@Test
public void redirect_with_context_path_when_failing_because_of_UnauthorizedExceptionException() throws Exception {
- when(server.getContextPath()).thenReturn("/sonarqube");
+ when(request.getContextPath()).thenReturn("/sonarqube");
FailWithUnauthorizedExceptionIdProvider identityProvider = new FailWithUnauthorizedExceptionIdProvider();
when(request.getRequestURI()).thenReturn("/sonarqube/oauth2/callback/" + identityProvider.getKey());
identityProviderRepository.addIdentityProvider(identityProvider);
underTest.doFilter(request, response, chain);
- verify(response).sendRedirect("/sonarqube/sessions/unauthorized?message=Email+john%40email.com+is+already+used");
+ verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
verify(oAuthRedirection).delete(eq(request), eq(response));
}
@@ -201,6 +210,18 @@ public class OAuth2CallbackFilterTest {
}
@Test
+ public void redirect_with_context_when_failing_because_of_Exception() throws Exception {
+ when(request.getContextPath()).thenReturn("/sonarqube");
+ FailWithIllegalStateException identityProvider = new FailWithIllegalStateException();
+ when(request.getRequestURI()).thenReturn("/oauth2/callback/" + identityProvider.getKey());
+ identityProviderRepository.addIdentityProvider(identityProvider);
+
+ underTest.doFilter(request, response, chain);
+
+ verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
+ }
+
+ @Test
public void redirect_when_failing_because_of_EmailAlreadyExistException() throws Exception {
UserDto existingUser = newUserDto().setEmail("john@email.com").setExternalLogin("john.bitbucket").setExternalIdentityProvider("bitbucket");
FailWithEmailAlreadyExistException identityProvider = new FailWithEmailAlreadyExistException(existingUser);
@@ -209,9 +230,16 @@ public class OAuth2CallbackFilterTest {
underTest.doFilter(request, response, chain);
- verify(response)
- .sendRedirect("/sessions/email_already_exists?email=john%40email.com&login=john.github&provider=failing&existingLogin=john.bitbucket&existingProvider=bitbucket");
+ verify(response).sendRedirect("/sessions/email_already_exists");
verify(oAuthRedirection).delete(eq(request), eq(response));
+ verify(response).addCookie(cookieArgumentCaptor.capture());
+ Cookie cookie = cookieArgumentCaptor.getValue();
+ assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
+ assertThat(cookie.getValue()).contains("john%40email.com");
+ assertThat(cookie.getPath()).isEqualTo("/");
+ assertThat(cookie.isHttpOnly()).isFalse();
+ assertThat(cookie.getMaxAge()).isEqualTo(300);
+ assertThat(cookie.getSecure()).isFalse();
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
index e25d6918b6e..cac1b7ceaf7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.authentication;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
@@ -58,6 +59,8 @@ public class UserSessionInitializerTest {
private RequestAuthenticator authenticator = mock(RequestAuthenticator.class);
private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
private MapSettings settings = new MapSettings();
+ private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
+
private UserSessionInitializer underTest = new UserSessionInitializer(settings.asConfig(), threadLocalSession, authenticationEvent, authenticator);
@Before
@@ -154,7 +157,15 @@ public class UserSessionInitializerTest {
assertThat(underTest.initUserSession(request, response)).isFalse();
- verify(response).sendRedirect("/sessions/unauthorized?message=Token+id+hasn%27t+been+found");
+ verify(response).sendRedirect("/sessions/unauthorized");
+ verify(response).addCookie(cookieArgumentCaptor.capture());
+ Cookie cookie = cookieArgumentCaptor.getValue();
+ assertThat(cookie.getName()).isEqualTo("AUTHENTICATION-ERROR");
+ assertThat(cookie.getValue()).isEqualTo("Token%20id%20hasn%27t%20been%20found");
+ assertThat(cookie.getPath()).isEqualTo("/");
+ assertThat(cookie.isHttpOnly()).isFalse();
+ assertThat(cookie.getMaxAge()).isEqualTo(300);
+ assertThat(cookie.getSecure()).isFalse();
}
@Test
@@ -165,7 +176,7 @@ public class UserSessionInitializerTest {
assertThat(underTest.initUserSession(request, response)).isFalse();
- verify(response).sendRedirect("/sonarqube/sessions/unauthorized?message=Token+id+hasn%27t+been+found");
+ verify(response).sendRedirect("/sonarqube/sessions/unauthorized");
}
private void assertPathIsIgnored(String path) {