diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-07-07 23:06:49 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-07-08 15:50:13 +0200 |
commit | be5552b150a28447800c588202a0cf78694bf3e3 (patch) | |
tree | 003737bd5c0e4d78a0b09116fb88aff59784eecf | |
parent | 3a27ef32e1d258a82d457d47bf9aed52390565bf (diff) | |
download | sonarqube-be5552b150a28447800c588202a0cf78694bf3e3.tar.gz sonarqube-be5552b150a28447800c588202a0cf78694bf3e3.zip |
SONAR-7856 Do not use "sonar.core.serverBaseURL" to detect if server is using SSL
14 files changed, 237 insertions, 232 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java index e62b3cb28ae..f04b06c2dc6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java @@ -79,7 +79,7 @@ public class BaseContextFactory { @Override public void authenticate(UserIdentity userIdentity) { UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider); - jwtHttpHandler.generateToken(userDto, response); + jwtHttpHandler.generateToken(userDto, request, response); threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java index 8814772aa79..02baa1c1b6c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java @@ -22,11 +22,15 @@ package org.sonar.server.authentication; import java.util.Arrays; import java.util.Optional; +import javax.annotation.Nullable; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; public class CookieUtils { + private static final String HTTPS_HEADER = "X-Forwarded-Proto"; + private static final String HTTPS_VALUE = "https"; + private CookieUtils() { // Only static methods } @@ -40,4 +44,20 @@ public class CookieUtils { .filter(cookie -> cookieName.equals(cookie.getName())) .findFirst(); } + + public static Cookie createCookie(String name, @Nullable String value, boolean httpOnly, int expiry, HttpServletRequest request) { + Cookie cookie = new Cookie(name, value); + // Path is set "/" in order to allow rails to be able to remove cookies + // TODO When logout when be implemented in Java (SONAR-7774), following line should be replaced by + // cookie.setPath(request.getContextPath()"/"); + cookie.setPath("/"); + cookie.setSecure(isHttps(request)); + cookie.setHttpOnly(httpOnly); + cookie.setMaxAge(expiry); + return cookie; + } + + private static boolean isHttps(HttpServletRequest request) { + return HTTPS_VALUE.equalsIgnoreCase(request.getHeader(HTTPS_HEADER)); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java index 01c2adc46b0..b3d2e014450 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java @@ -19,20 +19,19 @@ */ package org.sonar.server.authentication; -import static org.apache.commons.lang.StringUtils.isBlank; - import com.google.common.collect.ImmutableSet; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Set; import javax.annotation.Nullable; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; -import org.sonar.api.platform.Server; import org.sonar.server.exceptions.UnauthorizedException; +import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.server.authentication.CookieUtils.createCookie; + public class JwtCsrfVerifier { private static final String CSRF_STATE_COOKIE = "XSRF-TOKEN"; @@ -50,20 +49,13 @@ public class JwtCsrfVerifier { "/api/projects/create", "/api/properties/create", "/api/server", - "/api/user_properties" - ); - - private final Server server; + "/api/user_properties"); - public JwtCsrfVerifier(Server server) { - this.server = server; - } - - public String generateState(HttpServletResponse response, int timeoutInSeconds) { + public String generateState(HttpServletRequest request, HttpServletResponse response, int timeoutInSeconds) { // Create a state token to prevent request forgery. // Store it in the cookie for later validation. String state = new BigInteger(130, new SecureRandom()).toString(32); - response.addCookie(createCookie(state, timeoutInSeconds)); + response.addCookie(createCookie(CSRF_STATE_COOKIE, state, false, timeoutInSeconds, request)); return state; } @@ -77,12 +69,12 @@ public class JwtCsrfVerifier { } } - public void refreshState(HttpServletResponse response, String csrfState, int timeoutInSeconds){ - response.addCookie(createCookie(csrfState, timeoutInSeconds)); + public void refreshState(HttpServletRequest request, HttpServletResponse response, String csrfState, int timeoutInSeconds) { + response.addCookie(createCookie(CSRF_STATE_COOKIE, csrfState, false, timeoutInSeconds, request)); } - public void removeState(HttpServletResponse response){ - response.addCookie(createCookie(null, 0)); + public void removeState(HttpServletRequest request, HttpServletResponse response) { + response.addCookie(createCookie(CSRF_STATE_COOKIE, null, false, 0, request)); } private static boolean shouldRequestBeChecked(HttpServletRequest request) { @@ -94,22 +86,8 @@ public class JwtCsrfVerifier { return false; } - private static boolean isRailsWsUrl(String uri){ - for (String railsUrl : RAILS_UPDATE_API_URLS) { - if (uri.startsWith(railsUrl)) { - return true; - } - } - return false; - } - - private Cookie createCookie(@Nullable String csrfState, int timeoutInSeconds){ - Cookie cookie = new Cookie(CSRF_STATE_COOKIE, csrfState); - cookie.setPath("/"); - cookie.setSecure(server.isSecured()); - cookie.setHttpOnly(false); - cookie.setMaxAge(timeoutInSeconds); - return cookie; + private static boolean isRailsWsUrl(String uri) { + return RAILS_UPDATE_API_URLS.stream().filter(uri::startsWith).findFirst().isPresent(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java index ab847de238f..4ae491d5bbe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java @@ -20,10 +20,6 @@ package org.sonar.server.authentication; -import static java.util.Objects.requireNonNull; -import static org.elasticsearch.common.Strings.isNullOrEmpty; -import static org.sonar.server.authentication.CookieUtils.findCookie; - import com.google.common.collect.ImmutableMap; import io.jsonwebtoken.Claims; import java.util.Date; @@ -34,13 +30,16 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.time.DateUtils; import org.sonar.api.config.Settings; -import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.user.UserDto; +import static java.util.Objects.requireNonNull; +import static org.elasticsearch.common.Strings.isNullOrEmpty; +import static org.sonar.server.authentication.CookieUtils.findCookie; + @ServerSide public class JwtHttpHandler { @@ -61,24 +60,22 @@ public class JwtHttpHandler { private final System2 system2; private final DbClient dbClient; - private final Server server; private final JwtSerializer jwtSerializer; // This timeout is used to disconnect the user we he has not browse any page for a while private final int sessionTimeoutInSeconds; private final JwtCsrfVerifier jwtCsrfVerifier; - public JwtHttpHandler(System2 system2, DbClient dbClient, Server server, Settings settings, JwtSerializer jwtSerializer, JwtCsrfVerifier jwtCsrfVerifier) { + public JwtHttpHandler(System2 system2, DbClient dbClient, Settings settings, JwtSerializer jwtSerializer, JwtCsrfVerifier jwtCsrfVerifier) { this.jwtSerializer = jwtSerializer; - this.server = server; this.dbClient = dbClient; this.system2 = system2; this.sessionTimeoutInSeconds = getSessionTimeoutInSeconds(settings); this.jwtCsrfVerifier = jwtCsrfVerifier; } - public void generateToken(UserDto user, HttpServletResponse response) { - String csrfState = jwtCsrfVerifier.generateState(response, sessionTimeoutInSeconds); + public void generateToken(UserDto user, HttpServletRequest request, HttpServletResponse response) { + String csrfState = jwtCsrfVerifier.generateState(request, response, sessionTimeoutInSeconds); String token = jwtSerializer.encode(new JwtSerializer.JwtSession( user.getLogin(), @@ -86,7 +83,7 @@ public class JwtHttpHandler { ImmutableMap.of( LAST_REFRESH_TIME_PARAM, system2.now(), CSRF_JWT_PARAM, csrfState))); - response.addCookie(createCookie(JWT_COOKIE, token, sessionTimeoutInSeconds)); + response.addCookie(createCookie(request, JWT_COOKIE, token, sessionTimeoutInSeconds)); } public Optional<UserDto> validateToken(HttpServletRequest request, HttpServletResponse response) { @@ -132,7 +129,7 @@ public class JwtHttpHandler { jwtCsrfVerifier.verifyState(request, (String) token.get(CSRF_JWT_PARAM)); if (now.after(DateUtils.addSeconds(getLastRefreshDate(token), SESSION_REFRESH_IN_SECONDS))) { - refreshToken(token, response); + refreshToken(token, request, response); } Optional<UserDto> user = selectUserFromDb(token.getSubject()); @@ -148,24 +145,19 @@ public class JwtHttpHandler { return new Date(lastFreshTime); } - private void refreshToken(Claims token, HttpServletResponse response) { + private void refreshToken(Claims token, HttpServletRequest request, HttpServletResponse response) { String refreshToken = jwtSerializer.refresh(token, sessionTimeoutInSeconds); - response.addCookie(createCookie(JWT_COOKIE, refreshToken, sessionTimeoutInSeconds)); - jwtCsrfVerifier.refreshState(response, (String) token.get(CSRF_JWT_PARAM), sessionTimeoutInSeconds); + response.addCookie(createCookie(request, JWT_COOKIE, refreshToken, sessionTimeoutInSeconds)); + jwtCsrfVerifier.refreshState(request, response, (String) token.get(CSRF_JWT_PARAM), sessionTimeoutInSeconds); } - void removeToken(HttpServletResponse response) { - response.addCookie(createCookie(JWT_COOKIE, null, 0)); - jwtCsrfVerifier.removeState(response); + void removeToken(HttpServletRequest request, HttpServletResponse response) { + response.addCookie(createCookie(request, JWT_COOKIE, null, 0)); + jwtCsrfVerifier.removeState(request, response); } - private Cookie createCookie(String name, @Nullable String value, int expirationInSeconds) { - Cookie cookie = new Cookie(name, value); - cookie.setPath("/"); - cookie.setSecure(server.isSecured()); - cookie.setHttpOnly(true); - cookie.setMaxAge(expirationInSeconds); - return cookie; + private static Cookie createCookie(HttpServletRequest request, String name, @Nullable String value, int expirationInSeconds) { + return CookieUtils.createCookie(name, value, true, expirationInSeconds, request); } private Optional<UserDto> selectUserFromDb(String userLogin) { 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 18cbc0d8808..6f29dfc0712 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 @@ -19,10 +19,6 @@ */ package org.sonar.server.authentication; -import static java.lang.String.format; -import static org.sonar.api.CoreProperties.SERVER_BASE_URL; -import static org.sonar.server.authentication.OAuth2CallbackFilter.CALLBACK_PATH; - import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,6 +31,10 @@ import org.sonar.db.user.UserDto; import org.sonar.server.user.ServerUserSession; import org.sonar.server.user.ThreadLocalUserSession; +import static java.lang.String.format; +import static org.sonar.api.CoreProperties.SERVER_BASE_URL; +import static org.sonar.server.authentication.OAuth2CallbackFilter.CALLBACK_PATH; + public class OAuth2ContextFactory { private final DbClient dbClient; @@ -85,7 +85,7 @@ public class OAuth2ContextFactory { @Override public String generateCsrfState() { - return csrfVerifier.generateState(response); + return csrfVerifier.generateState(request, response); } @Override @@ -124,7 +124,7 @@ public class OAuth2ContextFactory { @Override public void authenticate(UserIdentity userIdentity) { UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider); - jwtHttpHandler.generateToken(userDto, response); + jwtHttpHandler.generateToken(userDto, request, response); threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java index 9870ada7c82..3c48a481439 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java @@ -19,59 +19,41 @@ */ package org.sonar.server.authentication; -import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; -import static org.apache.commons.lang.StringUtils.isBlank; - import java.math.BigInteger; import java.security.SecureRandom; -import java.util.Optional; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.sonar.api.platform.Server; import org.sonar.server.exceptions.UnauthorizedException; +import static java.lang.String.format; +import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; +import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.server.authentication.CookieUtils.createCookie; +import static org.sonar.server.authentication.CookieUtils.findCookie; + public class OAuthCsrfVerifier { private static final String CSRF_STATE_COOKIE = "OAUTHSTATE"; - private final Server server; - - public OAuthCsrfVerifier(Server server) { - this.server = server; - } - - public String generateState(HttpServletResponse response) { + public String generateState(HttpServletRequest request, HttpServletResponse response) { // Create a state token to prevent request forgery. // Store it in the session for later validation. String state = new BigInteger(130, new SecureRandom()).toString(32); - Cookie cookie = new Cookie(CSRF_STATE_COOKIE, sha256Hex(state)); - cookie.setPath(server.getContextPath() + "/"); - cookie.setHttpOnly(true); - cookie.setMaxAge(-1); - cookie.setSecure(server.isSecured()); - response.addCookie(cookie); + response.addCookie(createCookie(CSRF_STATE_COOKIE, sha256Hex(state), true, -1, request)); return state; } public void verifyState(HttpServletRequest request, HttpServletResponse response) { - Optional<Cookie> stateCookie = CookieUtils.findCookie(CSRF_STATE_COOKIE, request); - if (!stateCookie.isPresent()) { - throw new UnauthorizedException(); - } - Cookie cookie = stateCookie.get(); - + Cookie cookie = findCookie(CSRF_STATE_COOKIE, request).orElseThrow(() -> new UnauthorizedException(format("Cookie '%s' is missing", CSRF_STATE_COOKIE))); String hashInCookie = cookie.getValue(); // remove cookie - cookie.setValue(null); - cookie.setMaxAge(0); - cookie.setPath(server.getContextPath() + "/"); - response.addCookie(cookie); + response.addCookie(createCookie(CSRF_STATE_COOKIE, null, true, 0, request)); String stateInRequest = request.getParameter("state"); if (isBlank(stateInRequest) || !sha256Hex(stateInRequest).equals(hashInCookie)) { - throw new UnauthorizedException(); + throw new UnauthorizedException("CSRF state value is invalid"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java index 4407c90e50c..eb69f2b569d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java @@ -20,9 +20,6 @@ package org.sonar.server.authentication.ws; -import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; -import static org.elasticsearch.common.Strings.isNullOrEmpty; - import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -40,6 +37,9 @@ import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.user.ServerUserSession; import org.sonar.server.user.ThreadLocalUserSession; +import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static org.elasticsearch.common.Strings.isNullOrEmpty; + public class LoginAction extends ServletFilter { public static final String AUTH_LOGIN_URL = "/api/authentication/login"; @@ -74,7 +74,7 @@ public class LoginAction extends ServletFilter { } try { UserDto userDto = authenticate(request); - jwtHttpHandler.generateToken(userDto, response); + jwtHttpHandler.generateToken(userDto, request, response); threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto)); // TODO add chain.doFilter when Rack filter will not be executed after this filter (or use a Servlet) } catch (UnauthorizedException e) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java index cbe62a31472..4646ccc892b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java @@ -19,14 +19,6 @@ */ package org.sonar.server.authentication; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.sonar.db.user.UserTesting.newUserDto; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -44,6 +36,14 @@ import org.sonar.db.user.UserDto; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSession; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.db.user.UserTesting.newUserDto; + public class BaseContextFactoryTest { static String PUBLIC_ROOT_URL = "https://mydomain.com"; @@ -100,7 +100,7 @@ public class BaseContextFactoryTest { context.authenticate(USER_IDENTITY); verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider); - verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response)); + verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(request), eq(response)); verify(threadLocalUserSession).set(any(UserSession.class)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java new file mode 100644 index 00000000000..02489f17594 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CookieUtilsTest { + + private static final String HTTPS_HEADER = "X-Forwarded-Proto"; + + HttpServletRequest request = mock(HttpServletRequest.class); + + @Test + public void create_cookie() throws Exception { + Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); + assertThat(cookie.getName()).isEqualTo("name"); + assertThat(cookie.getValue()).isEqualTo("value"); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(10); + assertThat(cookie.getSecure()).isFalse(); + } + + @Test + public void create_not_secured_cookie_when_header_is_not_http() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("http"); + Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); + assertThat(cookie.getSecure()).isFalse(); + } + + @Test + public void create_secured_cookie_when_X_Forwarded_Proto_header_is_https() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("https"); + Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); + assertThat(cookie.getSecure()).isTrue(); + } + + @Test + public void create_secured_cookie_when_X_Forwarded_Proto_header_is_HTTPS() throws Exception { + when(request.getHeader(HTTPS_HEADER)).thenReturn("HTTPS"); + Cookie cookie = CookieUtils.createCookie("name", "value", true, 10, request); + assertThat(cookie.getSecure()).isTrue(); + } + + @Test + public void find_cookie() throws Exception { + Cookie cookie = new Cookie("name", "value"); + when(request.getCookies()).thenReturn(new Cookie[] {cookie}); + + assertThat(CookieUtils.findCookie("name", request)).isPresent(); + assertThat(CookieUtils.findCookie("NAME", request)).isEmpty(); + assertThat(CookieUtils.findCookie("unknown", request)).isEmpty(); + } + + @Test + public void does_not_fail_to_find_cookie_when_no_cookie() throws Exception { + assertThat(CookieUtils.findCookie("unknown", request)).isEmpty(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java index ab15b2d4027..65a3f7a2f19 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java @@ -19,11 +19,6 @@ */ package org.sonar.server.authentication; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,6 +30,11 @@ import org.mockito.ArgumentCaptor; import org.sonar.api.platform.Server; import org.sonar.server.exceptions.UnauthorizedException; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + public class JwtCsrfVerifierTest { @Rule @@ -50,7 +50,7 @@ public class JwtCsrfVerifierTest { HttpServletResponse response = mock(HttpServletResponse.class); HttpServletRequest request = mock(HttpServletRequest.class); - JwtCsrfVerifier underTest = new JwtCsrfVerifier(server); + JwtCsrfVerifier underTest = new JwtCsrfVerifier(); @Before public void setUp() throws Exception { @@ -58,26 +58,12 @@ public class JwtCsrfVerifierTest { } @Test - public void generate_state_on_secured_server() throws Exception { - when(server.isSecured()).thenReturn(true); - - String state = underTest.generateState(response, TIMEOUT); + public void generate_state() throws Exception { + String state = underTest.generateState(request, response, TIMEOUT); assertThat(state).isNotEmpty(); verify(response).addCookie(cookieArgumentCaptor.capture()); - verifyCookie(cookieArgumentCaptor.getValue(), true); - } - - @Test - public void generate_state_on_not_secured_server() throws Exception { - when(server.isSecured()).thenReturn(false); - - String state = underTest.generateState(response, TIMEOUT); - assertThat(state).isNotEmpty(); - - verify(response).addCookie(cookieArgumentCaptor.capture()); - - verifyCookie(cookieArgumentCaptor.getValue(), false); + verifyCookie(cookieArgumentCaptor.getValue()); } @Test @@ -175,19 +161,15 @@ public class JwtCsrfVerifierTest { @Test public void refresh_state() throws Exception { - when(server.isSecured()).thenReturn(true); - - underTest.refreshState(response, CSRF_STATE, 30); + underTest.refreshState(request, response, CSRF_STATE, 30); verify(response).addCookie(cookieArgumentCaptor.capture()); - verifyCookie(cookieArgumentCaptor.getValue(), true); + verifyCookie(cookieArgumentCaptor.getValue()); } @Test public void remove_state() throws Exception { - when(server.isSecured()).thenReturn(true); - - underTest.removeState(response); + underTest.removeState(request, response); verify(response).addCookie(cookieArgumentCaptor.capture()); Cookie cookie = cookieArgumentCaptor.getValue(); @@ -195,25 +177,25 @@ public class JwtCsrfVerifierTest { assertThat(cookie.getMaxAge()).isEqualTo(0); } - private void verifyCookie(Cookie cookie, boolean isSecured) { + private void verifyCookie(Cookie cookie) { assertThat(cookie.getName()).isEqualTo("XSRF-TOKEN"); assertThat(cookie.getValue()).isNotEmpty(); assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isFalse(); assertThat(cookie.getMaxAge()).isEqualTo(TIMEOUT); - assertThat(cookie.getSecure()).isEqualTo(isSecured); + assertThat(cookie.getSecure()).isFalse(); } - private void mockPostJavaWsRequest(){ + private void mockPostJavaWsRequest() { when(request.getRequestURI()).thenReturn(JAVA_WS_URL); when(request.getMethod()).thenReturn("POST"); } - private void mockRequestCsrf(String csrfState){ + private void mockRequestCsrf(String csrfState) { when(request.getHeader("X-XSRF-TOKEN")).thenReturn(csrfState); } - private void executeVerifyStateDoesNotFailOnRequest(String uri, String method){ + private void executeVerifyStateDoesNotFailOnRequest(String uri, String method) { when(request.getRequestURI()).thenReturn(uri); when(request.getMethod()).thenReturn(method); diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java index 0d0ce6cf511..b8646c4f652 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java @@ -20,19 +20,6 @@ package org.sonar.server.authentication; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; -import static org.sonar.api.utils.System2.INSTANCE; -import static org.sonar.db.user.UserTesting.newUserDto; - import io.jsonwebtoken.Claims; import io.jsonwebtoken.impl.DefaultClaims; import java.util.Date; @@ -48,13 +35,25 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import org.sonar.api.config.Settings; -import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.user.UserDto; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.System2.INSTANCE; +import static org.sonar.db.user.UserTesting.newUserDto; + public class JwtHttpHandlerTest { static final String JWT_TOKEN = "TOKEN"; @@ -84,29 +83,27 @@ public class JwtHttpHandlerTest { HttpSession httpSession = mock(HttpSession.class); System2 system2 = mock(System2.class); - Server server = mock(Server.class); Settings settings = new Settings(); JwtSerializer jwtSerializer = mock(JwtSerializer.class); JwtCsrfVerifier jwtCsrfVerifier = mock(JwtCsrfVerifier.class); UserDto userDto = newUserDto().setLogin(USER_LOGIN); - JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier); + JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, settings, jwtSerializer, jwtCsrfVerifier); @Before public void setUp() throws Exception { when(system2.now()).thenReturn(NOW); - when(server.isSecured()).thenReturn(true); when(request.getSession()).thenReturn(httpSession); when(jwtSerializer.encode(any(JwtSerializer.JwtSession.class))).thenReturn(JWT_TOKEN); - when(jwtCsrfVerifier.generateState(eq(response), anyInt())).thenReturn(CSRF_STATE); + when(jwtCsrfVerifier.generateState(eq(request), eq(response), anyInt())).thenReturn(CSRF_STATE); dbClient.userDao().insert(dbSession, userDto); dbSession.commit(); } @Test public void create_token() throws Exception { - underTest.generateToken(userDto, response); + underTest.generateToken(userDto, request, response); Optional<Cookie> jwtCookie = findCookie("JWT-SESSION"); assertThat(jwtCookie).isPresent(); @@ -118,9 +115,9 @@ public class JwtHttpHandlerTest { @Test public void generate_csrf_state_when_creating_token() throws Exception { - underTest.generateToken(userDto, response); + underTest.generateToken(userDto, request, response); - verify(jwtCsrfVerifier).generateState(response, 3 * 24 * 60 * 60); + verify(jwtCsrfVerifier).generateState(request, response, 3 * 24 * 60 * 60); verify(jwtSerializer).encode(jwtArgumentCaptor.capture()); JwtSerializer.JwtSession token = jwtArgumentCaptor.getValue(); @@ -132,8 +129,8 @@ public class JwtHttpHandlerTest { int sessionTimeoutInHours = 10; settings.setProperty("sonar.auth.sessionTimeoutInHours", sessionTimeoutInHours); - underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier); - underTest.generateToken(userDto, response); + underTest = new JwtHttpHandler(system2, dbClient, settings, jwtSerializer, jwtCsrfVerifier); + underTest.generateToken(userDto, request, response); verify(jwtSerializer).encode(jwtArgumentCaptor.capture()); verifyToken(jwtArgumentCaptor.getValue(), sessionTimeoutInHours * 60 * 60, NOW); @@ -144,12 +141,12 @@ public class JwtHttpHandlerTest { int firstSessionTimeoutInHours = 10; settings.setProperty("sonar.auth.sessionTimeoutInHours", firstSessionTimeoutInHours); - underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier); - underTest.generateToken(userDto, response); + underTest = new JwtHttpHandler(system2, dbClient, settings, jwtSerializer, jwtCsrfVerifier); + underTest.generateToken(userDto, request, response); // The property is updated, but it won't be taking into account settings.setProperty("sonar.auth.sessionTimeoutInHours", 15); - underTest.generateToken(userDto, response); + underTest.generateToken(userDto, request, response); verify(jwtSerializer, times(2)).encode(jwtArgumentCaptor.capture()); verifyToken(jwtArgumentCaptor.getAllValues().get(0), firstSessionTimeoutInHours * 60 * 60, NOW); verifyToken(jwtArgumentCaptor.getAllValues().get(1), firstSessionTimeoutInHours * 60 * 60, NOW); @@ -270,15 +267,15 @@ public class JwtHttpHandlerTest { underTest.validateToken(request, response); verify(jwtSerializer).refresh(any(Claims.class), anyInt()); - verify(jwtCsrfVerifier).refreshState(response, "CSRF_STATE", 3 * 24 * 60 * 60); + verify(jwtCsrfVerifier).refreshState(request, response, "CSRF_STATE", 3 * 24 * 60 * 60); } @Test public void remove_token() throws Exception { - underTest.removeToken(response); + underTest.removeToken(request, response); verifyCookie(findCookie("JWT-SESSION").get(), null, 0); - verify(jwtCsrfVerifier).removeState(response); + verify(jwtCsrfVerifier).removeState(request, response); } private void verifyToken(JwtSerializer.JwtSession token, int expectedExpirationTime, long expectedRefreshTime) { @@ -298,7 +295,7 @@ public class JwtHttpHandlerTest { assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(expiry); - assertThat(cookie.getSecure()).isEqualTo(true); + assertThat(cookie.getSecure()).isFalse(); assertThat(cookie.getValue()).isEqualTo(value); } 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 46dc9582542..57af73855cd 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,14 +19,6 @@ */ package org.sonar.server.authentication; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.sonar.db.user.UserTesting.newUserDto; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -46,6 +38,14 @@ import org.sonar.db.user.UserDto; import org.sonar.server.user.ThreadLocalUserSession; import org.sonar.server.user.UserSession; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.db.user.UserTesting.newUserDto; + public class OAuth2ContextFactoryTest { @Rule @@ -110,7 +110,7 @@ public class OAuth2ContextFactoryTest { context.generateCsrfState(); - verify(csrfVerifier).generateState(response); + verify(csrfVerifier).generateState(request, response); } @Test @@ -151,7 +151,7 @@ public class OAuth2ContextFactoryTest { callback.authenticate(USER_IDENTITY); verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider); - verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response)); + verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(request), eq(response)); verify(threadLocalUserSession).set(any(UserSession.class)); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java index 120f8d1a298..07815176e20 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java @@ -19,13 +19,6 @@ */ package org.sonar.server.authentication; -import static org.apache.commons.codec.digest.DigestUtils.sha1Hex; -import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -37,6 +30,13 @@ import org.mockito.ArgumentCaptor; import org.sonar.api.platform.Server; import org.sonar.server.exceptions.UnauthorizedException; +import static org.apache.commons.codec.digest.DigestUtils.sha1Hex; +import static org.apache.commons.codec.digest.DigestUtils.sha256Hex; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + public class OAuthCsrfVerifierTest { @Rule @@ -48,7 +48,7 @@ public class OAuthCsrfVerifierTest { HttpServletResponse response = mock(HttpServletResponse.class); HttpServletRequest request = mock(HttpServletRequest.class); - OAuthCsrfVerifier underTest = new OAuthCsrfVerifier(server); + OAuthCsrfVerifier underTest = new OAuthCsrfVerifier(); @Before public void setUp() throws Exception { @@ -56,27 +56,13 @@ public class OAuthCsrfVerifierTest { } @Test - public void generate_state_on_secured_server() throws Exception { - when(server.isSecured()).thenReturn(true); - - String state = underTest.generateState(response); - assertThat(state).isNotEmpty(); - - verify(response).addCookie(cookieArgumentCaptor.capture()); - - verifyCookie(cookieArgumentCaptor.getValue(), true); - } - - @Test - public void generate_state_on_not_secured_server() throws Exception { - when(server.isSecured()).thenReturn(false); - - String state = underTest.generateState(response); + public void generate_state() throws Exception { + String state = underTest.generateState(request, response); assertThat(state).isNotEmpty(); verify(response).addCookie(cookieArgumentCaptor.capture()); - verifyCookie(cookieArgumentCaptor.getValue(), false); + verifyCookie(cookieArgumentCaptor.getValue()); } @Test @@ -96,20 +82,6 @@ public class OAuthCsrfVerifierTest { } @Test - public void verify_state_when_context() throws Exception { - String state = "state"; - when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha256Hex(state))}); - when(request.getParameter("state")).thenReturn(state); - when(server.getContextPath()).thenReturn("/sonarqube"); - - underTest.verifyState(request, response); - - verify(response).addCookie(cookieArgumentCaptor.capture()); - Cookie updatedCookie = cookieArgumentCaptor.getValue(); - assertThat(updatedCookie.getPath()).isEqualTo("/sonarqube/"); - } - - @Test public void fail_with_unauthorized_when_state_cookie_is_not_the_same_as_state_parameter() throws Exception { when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha1Hex("state"))}); when(request.getParameter("state")).thenReturn("other value"); @@ -136,12 +108,12 @@ public class OAuthCsrfVerifierTest { underTest.verifyState(request, response); } - private void verifyCookie(Cookie cookie, boolean isSecured) { + private void verifyCookie(Cookie cookie) { assertThat(cookie.getName()).isEqualTo("OAUTHSTATE"); assertThat(cookie.getValue()).isNotEmpty(); assertThat(cookie.getPath()).isEqualTo("/"); assertThat(cookie.isHttpOnly()).isTrue(); assertThat(cookie.getMaxAge()).isEqualTo(-1); - assertThat(cookie.getSecure()).isEqualTo(isSecured); + assertThat(cookie.getSecure()).isFalse(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java index 8e5c664f5b8..ce7131d69db 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java @@ -20,13 +20,6 @@ package org.sonar.server.authentication.ws; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -46,6 +39,13 @@ import org.sonar.server.authentication.JwtHttpHandler; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.user.ThreadLocalUserSession; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + public class LoginActionTest { static final String LOGIN = "LOGIN"; @@ -93,7 +93,7 @@ public class LoginActionTest { assertThat(threadLocalUserSession.isLoggedIn()).isTrue(); verify(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request); - verify(jwtHttpHandler).generateToken(user, response); + verify(jwtHttpHandler).generateToken(user, request, response); verifyZeroInteractions(chain); } |