@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));
}
}
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
}
.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));
+ }
}
*/
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";
"/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;
}
}
}
- 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) {
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();
}
}
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;
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 {
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(),
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) {
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());
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) {
*/
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;
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;
@Override
public String generateCsrfState() {
- return csrfVerifier.generateState(response);
+ return csrfVerifier.generateState(request, response);
}
@Override
@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));
}
}
*/
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");
}
}
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;
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";
}
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) {
*/
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;
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";
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));
}
}
--- /dev/null
+/*
+ * 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();
+ }
+}
*/
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;
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
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 {
}
@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
@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();
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);
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;
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";
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();
@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();
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);
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);
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) {
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);
}
*/
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;
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
context.generateCsrfState();
- verify(csrfVerifier).generateState(response);
+ verify(csrfVerifier).generateState(request, response);
}
@Test
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));
}
*/
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;
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
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 {
}
@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
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"))});
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();
}
}
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;
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";
assertThat(threadLocalUserSession.isLoggedIn()).isTrue();
verify(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
- verify(jwtHttpHandler).generateToken(user, response);
+ verify(jwtHttpHandler).generateToken(user, request, response);
verifyZeroInteractions(chain);
}