]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7856 Do not use "sonar.core.serverBaseURL" to detect if server is using SSL 1090/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 7 Jul 2016 21:06:49 +0000 (23:06 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 8 Jul 2016 13:50:13 +0000 (15:50 +0200)
14 files changed:
server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java
server/sonar-server/src/main/java/org/sonar/server/authentication/CookieUtils.java
server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java
server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java
server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java
server/sonar-server/src/main/java/org/sonar/server/authentication/OAuthCsrfVerifier.java
server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java
server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/CookieUtilsTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/OAuthCsrfVerifierTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java

index e62b3cb28ae24d1954b61e89ec4f778dfa63e475..f04b06c2dc6c43df1dba0319e4def7c614ddaa5c 100644 (file)
@@ -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));
     }
   }
index 8814772aa798f39f26946b59be7e738fa85468e5..02baa1c1b6c0cae7c54c51ebc119270950f40e0c 100644 (file)
@@ -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));
+  }
 }
index 01c2adc46b088e8e24f8d463c38e19609126f5ee..b3d2e0144507a3f74d343f95b54bef327c30c462 100644 (file)
  */
 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();
   }
 
 }
index ab847de238f7dae952bee72de055a3bde36ff2f7..4ae491d5bbefc6d99b8ffccf7fb9251896fb715d 100644 (file)
 
 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) {
index 18cbc0d8808f924dc4bab67a01a42cde139669a1..6f29dfc07128108aa7401b3926d82b57ea5e0eae 100644 (file)
  */
 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));
     }
   }
index 9870ada7c8275b6f27dc84f0f474395d84007ac6..3c48a481439e37635927669782c7394db44a8125 100644 (file)
  */
 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");
     }
   }
 
index 4407c90e50cfb657073a1bf5685c5ac0ac0f57bf..eb69f2b569d80aabd0f0ee285b834c0480232d2c 100644 (file)
@@ -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) {
index cbe62a314723197d9cebdf8ed07006ae55beab33..4646ccc892b35625f5be5fcc05e04093fd5ff291 100644 (file)
  */
 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 (file)
index 0000000..02489f1
--- /dev/null
@@ -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();
+  }
+}
index ab15b2d4027dfe1f2c11422885d521ae74ba051c..65a3f7a2f19077194c8f2a6d1236d03d4621bc3e 100644 (file)
  */
 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);
 
index 0d0ce6cf511c6d19830e93a5fac0149a968e40a0..b8646c4f6527eb1497b8b8c7ac5a98ab4985ff9e 100644 (file)
 
 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);
   }
 
index 46dc9582542d830248b8459fde34e30b4b59de44..57af73855cd5262a3b7ac3775a6ba9ee9c3f4f30 100644 (file)
  */
 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));
   }
 
index 120f8d1a298a2f2880d3cdc8e89f98f9366125ab..07815176e2059551852a4fad8dddef7ec3c90d92 100644 (file)
  */
 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
@@ -95,20 +81,6 @@ public class OAuthCsrfVerifierTest {
     assertThat(updatedCookie.getMaxAge()).isEqualTo(0);
   }
 
-  @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"))});
@@ -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();
   }
 }
index 8e5c664f5b8d6a5d01e16beac40cb184f42d477e..ce7131d69dbd63f4b6f482727ee4bc039b1dcd1a 100644 (file)
 
 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);
   }