aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2016-06-21 14:03:15 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2016-06-29 08:39:32 +0200
commitd82358c63d0fb979fb3cc27429a42ec833dc161a (patch)
tree0aa9f14c9def2eaf3be840009c80dfd4520fc881 /server
parentcd8f9c0a390181fb1bf25a6e9e3bab509dd7ebc7 (diff)
downloadsonarqube-d82358c63d0fb979fb3cc27429a42ec833dc161a.tar.gz
sonarqube-d82358c63d0fb979fb3cc27429a42ec833dc161a.zip
SONAR-7763 Allow authentication using basic HTTP authentication in Java
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/BasicAuthenticator.java104
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/JwtHttpHandler.java42
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java (renamed from server/sonar-server/src/main/java/org/sonar/server/authentication/ValidateJwtTokenFilter.java)107
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java (renamed from server/sonar-server/src/main/java/org/sonar/server/authentication/AuthLoginAction.java)18
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/UserSessionFilter.java53
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java24
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/BasicAuthenticatorTest.java162
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/JwtHttpHandlerTest.java51
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java22
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java198
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/ValidateJwtTokenFilterTest.java149
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/authentication/AuthLoginActionTest.java)39
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java72
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java13
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb27
19 files changed, 750 insertions, 367 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
index a55033f0e7e..db451df6810 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
@@ -21,6 +21,7 @@ package org.sonar.server.authentication;
import org.sonar.core.platform.Module;
import org.sonar.server.authentication.ws.AuthenticationWs;
+import org.sonar.server.authentication.ws.LoginAction;
public class AuthenticationModule extends Module {
@Override
@@ -34,12 +35,13 @@ public class AuthenticationModule extends Module {
OAuth2ContextFactory.class,
UserIdentityAuthenticator.class,
OAuthCsrfVerifier.class,
- ValidateJwtTokenFilter.class,
+ UserSessionInitializer.class,
JwtSerializer.class,
JwtHttpHandler.class,
JwtCsrfVerifier.class,
- AuthLoginAction.class,
+ LoginAction.class,
CredentialsAuthenticator.class,
- RealmAuthenticator.class);
+ RealmAuthenticator.class,
+ BasicAuthenticator.class);
}
}
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 ea7df897a90..e62b3cb28ae 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
@@ -24,18 +24,26 @@ import javax.servlet.http.HttpServletResponse;
import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
public class BaseContextFactory {
+ private final DbClient dbClient;
+ private final ThreadLocalUserSession threadLocalUserSession;
private final UserIdentityAuthenticator userIdentityAuthenticator;
private final Server server;
private final JwtHttpHandler jwtHttpHandler;
- public BaseContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, JwtHttpHandler jwtHttpHandler) {
+ public BaseContextFactory(DbClient dbClient, UserIdentityAuthenticator userIdentityAuthenticator, Server server, JwtHttpHandler jwtHttpHandler,
+ ThreadLocalUserSession threadLocalUserSession) {
+ this.dbClient = dbClient;
this.userIdentityAuthenticator = userIdentityAuthenticator;
this.server = server;
this.jwtHttpHandler = jwtHttpHandler;
+ this.threadLocalUserSession = threadLocalUserSession;
}
public BaseIdentityProvider.Context newContext(HttpServletRequest request, HttpServletResponse response, BaseIdentityProvider identityProvider) {
@@ -72,6 +80,7 @@ public class BaseContextFactory {
public void authenticate(UserIdentity userIdentity) {
UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
jwtHttpHandler.generateToken(userDto, response);
+ threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/BasicAuthenticator.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/BasicAuthenticator.java
new file mode 100644
index 00000000000..3c000d9b467
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/BasicAuthenticator.java
@@ -0,0 +1,104 @@
+/*
+ * 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 static java.util.Locale.ENGLISH;
+import static org.elasticsearch.common.Strings.isEmpty;
+
+import com.google.common.base.Charsets;
+import java.util.Base64;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.usertoken.UserTokenAuthenticator;
+
+public class BasicAuthenticator {
+
+ private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
+
+ private static final String AUTHORIZATION_HEADER = "Authorization";
+ private static final String BASIC_AUTHORIZATION = "BASIC";
+
+ private final DbClient dbClient;
+ private final CredentialsAuthenticator credentialsAuthenticator;
+ private final UserTokenAuthenticator userTokenAuthenticator;
+
+ public BasicAuthenticator(DbClient dbClient, CredentialsAuthenticator credentialsAuthenticator,
+ UserTokenAuthenticator userTokenAuthenticator) {
+ this.dbClient = dbClient;
+ this.credentialsAuthenticator = credentialsAuthenticator;
+ this.userTokenAuthenticator = userTokenAuthenticator;
+ }
+
+ public Optional<UserDto> authenticate(HttpServletRequest request) {
+ String authorizationHeader = request.getHeader(AUTHORIZATION_HEADER);
+ if (authorizationHeader == null || !authorizationHeader.toUpperCase(ENGLISH).startsWith(BASIC_AUTHORIZATION)) {
+ return Optional.empty();
+ }
+
+ String[] credentials = getCredentials(authorizationHeader);
+ String login = credentials[0];
+ String password = credentials[1];
+ return Optional.of(authenticate(login, password, request));
+ }
+
+ private static String[] getCredentials(String authorizationHeader) {
+ String basicAuthEncoded = authorizationHeader.substring(6);
+ String basicAuthDecoded = new String(BASE64_DECODER.decode(basicAuthEncoded.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
+
+ int semiColonPos = basicAuthDecoded.indexOf(':');
+ if (semiColonPos <= 0) {
+ throw new UnauthorizedException("Invalid credentials : " + basicAuthDecoded);
+ }
+ String login = basicAuthDecoded.substring(0, semiColonPos);
+ String password = basicAuthDecoded.substring(semiColonPos + 1);
+ return new String[] {login, password};
+ }
+
+ private UserDto authenticate(String login, String password, HttpServletRequest request) {
+ if (isEmpty(password)) {
+ return authenticateFromUserToken(login);
+ } else {
+ return credentialsAuthenticator.authenticate(login, password, request);
+ }
+ }
+
+ private UserDto authenticateFromUserToken(String token) {
+ Optional<String> authenticatedLogin = userTokenAuthenticator.authenticate(token);
+ if (!authenticatedLogin.isPresent()) {
+ throw new UnauthorizedException("Token doesn't exist");
+ }
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ UserDto userDto = dbClient.userDao().selectActiveUserByLogin(dbSession, authenticatedLogin.get());
+ if (userDto == null) {
+ throw new UnauthorizedException("User doesn't exist");
+ }
+ return userDto;
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+}
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 d42349b5f6e..2e8e88e258f 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
@@ -23,7 +23,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 static org.sonar.server.user.ServerUserSession.createForUser;
import com.google.common.collect.ImmutableMap;
import io.jsonwebtoken.Claims;
@@ -41,9 +40,6 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
-import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.user.ServerUserSession;
-import org.sonar.server.user.ThreadLocalUserSession;
@ServerSide
public class JwtHttpHandler {
@@ -71,20 +67,17 @@ public class JwtHttpHandler {
// 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;
- private final ThreadLocalUserSession threadLocalUserSession;
- public JwtHttpHandler(System2 system2, DbClient dbClient, Server server, Settings settings, JwtSerializer jwtSerializer, JwtCsrfVerifier jwtCsrfVerifier,
- ThreadLocalUserSession threadLocalUserSession) {
+ public JwtHttpHandler(System2 system2, DbClient dbClient, Server server, 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;
- this.threadLocalUserSession = threadLocalUserSession;
}
- void generateToken(UserDto user, HttpServletResponse response) {
+ public void generateToken(UserDto user, HttpServletResponse response) {
String csrfState = jwtCsrfVerifier.generateState(response, sessionTimeoutInSeconds);
String token = jwtSerializer.encode(new JwtSerializer.JwtSession(
@@ -94,22 +87,23 @@ public class JwtHttpHandler {
LAST_REFRESH_TIME_PARAM, system2.now(),
CSRF_JWT_PARAM, csrfState)));
response.addCookie(createCookie(JWT_COOKIE, token, sessionTimeoutInSeconds));
- threadLocalUserSession.set(createForUser(dbClient, user));
}
- void validateToken(HttpServletRequest request, HttpServletResponse response) {
- validate(request, response);
- if (!threadLocalUserSession.isLoggedIn()) {
- threadLocalUserSession.set(ServerUserSession.createForAnonymous(dbClient));
+ public Optional<UserDto> validateToken(HttpServletRequest request, HttpServletResponse response) {
+ Optional<UserDto> userDto = validate(request, response);
+ if (userDto.isPresent()) {
+ return userDto;
}
+ removeToken(response);
+ return Optional.empty();
}
- private void validate(HttpServletRequest request, HttpServletResponse response) {
+ private Optional<UserDto> validate(HttpServletRequest request, HttpServletResponse response) {
Optional<String> token = getTokenFromCookie(request);
if (!token.isPresent()) {
- return;
+ return Optional.empty();
}
- validateToken(token.get(), request, response);
+ return validateToken(token.get(), request, response);
}
private static Optional<String> getTokenFromCookie(HttpServletRequest request) {
@@ -125,18 +119,16 @@ public class JwtHttpHandler {
return Optional.of(token);
}
- private void validateToken(String tokenEncoded, HttpServletRequest request, HttpServletResponse response) {
+ private Optional<UserDto> validateToken(String tokenEncoded, HttpServletRequest request, HttpServletResponse response) {
Optional<Claims> claims = jwtSerializer.decode(tokenEncoded);
if (!claims.isPresent()) {
- removeToken(response);
- return;
+ return Optional.empty();
}
Date now = new Date(system2.now());
Claims token = claims.get();
if (now.after(DateUtils.addSeconds(token.getIssuedAt(), SESSION_DISCONNECT_IN_SECONDS))) {
- removeToken(response);
- return;
+ return Optional.empty();
}
jwtCsrfVerifier.verifyState(request, (String) token.get(CSRF_JWT_PARAM));
@@ -146,10 +138,9 @@ public class JwtHttpHandler {
Optional<UserDto> user = selectUserFromDb(token.getSubject());
if (!user.isPresent()) {
- removeToken(response);
- throw new UnauthorizedException("User does not exist");
+ return Optional.empty();
}
- threadLocalUserSession.set(createForUser(dbClient, user.get()));
+ return Optional.of(user.get());
}
private static Date getLastRefreshDate(Claims token) {
@@ -167,7 +158,6 @@ public class JwtHttpHandler {
void removeToken(HttpServletResponse response) {
response.addCookie(createCookie(JWT_COOKIE, null, 0));
jwtCsrfVerifier.removeState(response);
- threadLocalUserSession.remove();
}
private Cookie createCookie(String name, @Nullable String value, int expirationInSeconds) {
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 6f5948cc616..18cbc0d8808 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
@@ -30,16 +30,24 @@ import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.api.utils.MessageException;
+import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
public class OAuth2ContextFactory {
+ private final DbClient dbClient;
+ private final ThreadLocalUserSession threadLocalUserSession;
private final UserIdentityAuthenticator userIdentityAuthenticator;
private final Server server;
private final OAuthCsrfVerifier csrfVerifier;
private final JwtHttpHandler jwtHttpHandler;
- public OAuth2ContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler) {
+ public OAuth2ContextFactory(DbClient dbClient, ThreadLocalUserSession threadLocalUserSession, UserIdentityAuthenticator userIdentityAuthenticator, Server server,
+ OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler) {
+ this.dbClient = dbClient;
+ this.threadLocalUserSession = threadLocalUserSession;
this.userIdentityAuthenticator = userIdentityAuthenticator;
this.server = server;
this.csrfVerifier = csrfVerifier;
@@ -117,6 +125,7 @@ public class OAuth2ContextFactory {
public void authenticate(UserIdentity userIdentity) {
UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
jwtHttpHandler.generateToken(userDto, response);
+ threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/ValidateJwtTokenFilter.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
index fd3878b665e..5aa8b8e2d3b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/ValidateJwtTokenFilter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
@@ -22,104 +22,105 @@ package org.sonar.server.authentication;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
+import static org.sonar.api.web.ServletFilter.UrlPattern;
import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
-import static org.sonar.server.authentication.AuthLoginAction.AUTH_LOGIN_URL;
+import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL;
+import static org.sonar.server.user.ServerUserSession.createForAnonymous;
+import static org.sonar.server.user.ServerUserSession.createForUser;
import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
+import java.util.Optional;
import java.util.Set;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.api.config.Settings;
import org.sonar.api.server.ServerSide;
-import org.sonar.api.web.ServletFilter;
+import org.sonar.db.DbClient;
+import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.user.UserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
@ServerSide
-public class ValidateJwtTokenFilter extends ServletFilter {
+public class UserSessionInitializer {
// SONAR-6546 these urls should be get from WebService
private static final Set<String> SKIPPED_URLS = ImmutableSet.of(
- "/batch/index", "/batch/file", "/batch_bootstrap/index",
+ "/batch/index", "/batch/file",
"/maintenance/*",
"/setup/*",
"/sessions/*",
"/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
"/api/server/*",
- AUTH_LOGIN_URL
- );
+ AUTH_LOGIN_URL);
+ private static final UrlPattern URL_PATTERN = UrlPattern.builder()
+ .includes("/*")
+ .excludes(staticResourcePatterns())
+ .excludes(SKIPPED_URLS)
+ .build();
+
+ private final DbClient dbClient;
private final Settings settings;
private final JwtHttpHandler jwtHttpHandler;
- private final UserSession userSession;
+ private final BasicAuthenticator basicAuthenticator;
+ private final ThreadLocalUserSession userSession;
- public ValidateJwtTokenFilter(Settings settings, JwtHttpHandler jwtHttpHandler, UserSession userSession) {
+ public UserSessionInitializer(DbClient dbClient, Settings settings, JwtHttpHandler jwtHttpHandler, BasicAuthenticator basicAuthenticator,
+ ThreadLocalUserSession userSession) {
+ this.dbClient = dbClient;
this.settings = settings;
this.jwtHttpHandler = jwtHttpHandler;
+ this.basicAuthenticator = basicAuthenticator;
this.userSession = userSession;
}
- @Override
- public UrlPattern doGetPattern() {
- return UrlPattern.builder()
- .includes("/*")
- .excludes(staticResourcePatterns())
- .excludes(SKIPPED_URLS)
- .build();
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- HttpServletResponse response = (HttpServletResponse) servletResponse;
+ public boolean initUserSession(HttpServletRequest request, HttpServletResponse response) {
String path = request.getRequestURI().replaceFirst(request.getContextPath(), "");
-
try {
- if (isDeprecatedBatchWs(path)) {
- chain.doFilter(request, response);
- return;
+ // Do not set user session when url is excluded
+ if (!URL_PATTERN.matches(path)) {
+ return true;
}
-
- jwtHttpHandler.validateToken(request, response);
- // TODO handle basic authentication
- if (!userSession.isLoggedIn() && settings.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY)) {
- throw new UnauthorizedException("User must be authenticated");
- }
- chain.doFilter(request, response);
+ setUserSession(request, response);
+ return true;
} catch (UnauthorizedException e) {
jwtHttpHandler.removeToken(response);
response.setStatus(HTTP_UNAUTHORIZED);
-
if (isWsUrl(path)) {
- return;
+ return false;
}
// WS should stop here. Rails page should continue in order to deal with redirection
- chain.doFilter(request, response);
+ return true;
}
}
- // Scanner is still using deprecated /batch/<File name>.jar WS
- private static boolean isDeprecatedBatchWs(String path){
- return path.startsWith("/batch/") && path.endsWith(".jar");
+ private void setUserSession(HttpServletRequest request, HttpServletResponse response) {
+ Optional<UserDto> user = authenticate(request, response);
+ if (user.isPresent()) {
+ userSession.set(createForUser(dbClient, user.get()));
+ } else {
+ if (settings.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY)) {
+ throw new UnauthorizedException("User must be authenticated");
+ }
+ userSession.set(createForAnonymous(dbClient));
+ }
}
- private static boolean isWsUrl(String path){
- return path.startsWith("/batch/") || path.startsWith("/api/");
+ public void removeUserSession() {
+ userSession.remove();
}
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // Nothing to do
+ // Try first to authenticate from JWT token, then try from basic http header
+ private Optional<UserDto> authenticate(HttpServletRequest request, HttpServletResponse response) {
+ Optional<UserDto> user = jwtHttpHandler.validateToken(request, response);
+ if (user.isPresent()) {
+ return user;
+ }
+ return basicAuthenticator.authenticate(request);
}
- @Override
- public void destroy() {
- // Nothing to do
+ private static boolean isWsUrl(String path) {
+ return path.startsWith("/batch/") || path.startsWith("/api/");
}
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthLoginAction.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java
index 770cd6329ec..4407c90e50c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/authentication/AuthLoginAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/ws/LoginAction.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.authentication;
+package org.sonar.server.authentication.ws;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static org.elasticsearch.common.Strings.isNullOrEmpty;
@@ -32,21 +32,30 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.api.web.ServletFilter;
+import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.CredentialsAuthenticator;
+import org.sonar.server.authentication.JwtHttpHandler;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
-public class AuthLoginAction extends ServletFilter {
+public class LoginAction extends ServletFilter {
- static final String AUTH_LOGIN_URL = "/api/authentication/login";
+ public static final String AUTH_LOGIN_URL = "/api/authentication/login";
private static final String POST = "POST";
+ private final DbClient dbClient;
private final CredentialsAuthenticator credentialsAuthenticator;
private final JwtHttpHandler jwtHttpHandler;
+ private final ThreadLocalUserSession threadLocalUserSession;
- public AuthLoginAction(CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler) {
+ public LoginAction(DbClient dbClient, CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler, ThreadLocalUserSession threadLocalUserSession) {
+ this.dbClient = dbClient;
this.credentialsAuthenticator = credentialsAuthenticator;
this.jwtHttpHandler = jwtHttpHandler;
+ this.threadLocalUserSession = threadLocalUserSession;
}
@Override
@@ -66,6 +75,7 @@ public class AuthLoginAction extends ServletFilter {
try {
UserDto userDto = authenticate(request);
jwtHttpHandler.generateToken(userDto, 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) {
response.setStatus(e.httpCode());
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSessionFilter.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSessionFilter.java
index f5b7ccc4afc..58c351e6ea2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSessionFilter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSessionFilter.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.user;
+import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -26,44 +27,58 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-import org.sonar.api.utils.log.Loggers;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.sonar.server.authentication.UserSessionInitializer;
import org.sonar.server.platform.Platform;
-/**
- * @since 3.6
- */
public class UserSessionFilter implements Filter {
+
private final Platform platform;
+ private UserSessionInitializer userSessionInitializer;
public UserSessionFilter() {
this.platform = Platform.getInstance();
}
- public UserSessionFilter(Platform platform) {
+ @VisibleForTesting
+ UserSessionFilter(Platform platform) {
this.platform = platform;
}
@Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // nothing to do
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+ try {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ init();
+ if (!isInitialized() || userSessionInitializer.initUserSession(request, response)) {
+ chain.doFilter(servletRequest, servletResponse);
+ }
+ } finally {
+ if (isInitialized()) {
+ userSessionInitializer.removeUserSession();
+ }
+ }
+ }
+
+ private boolean isInitialized() {
+ return userSessionInitializer != null;
+ }
+
+ private void init() {
+ if (userSessionInitializer == null) {
+ userSessionInitializer = platform.getContainer().getComponentByType(UserSessionInitializer.class);
+ }
}
@Override
- public void destroy() {
+ public void init(FilterConfig filterConfig) throws ServletException {
// nothing to do
}
@Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
- try {
- chain.doFilter(servletRequest, servletResponse);
- } finally {
- ThreadLocalUserSession userSession = platform.getContainer().getComponentByType(ThreadLocalUserSession.class);
- if (userSession == null) {
- Loggers.get(UserSessionFilter.class).error("Can not retrieve ThreadLocalUserSession from Platform");
- } else {
- userSession.remove();
- }
- }
+ public void destroy() {
+ // nothing to do
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java b/server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java
index fb26c6f5c78..bb751b20ae9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java
@@ -38,15 +38,15 @@ public class UserTokenAuthenticator {
* The returned login is not validated. If database is corrupted (table USER_TOKENS badly purged
* for instance), then the login may not relate to a valid user.
*/
- public Optional<String> authenticate(String token) {
+ public java.util.Optional<String> authenticate(String token) {
String tokenHash = tokenGenerator.hash(token);
DbSession dbSession = dbClient.openSession(false);
try {
Optional<UserTokenDto> userToken = dbClient.userTokenDao().selectByTokenHash(dbSession, tokenHash);
if (userToken.isPresent()) {
- return Optional.of(userToken.get().getLogin());
+ return java.util.Optional.of(userToken.get().getLogin());
}
- return Optional.absent();
+ return java.util.Optional.empty();
} finally {
dbClient.closeSession(dbSession);
}
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 8128b3ac55e..cbe62a31472 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
@@ -25,16 +25,24 @@ 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.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
+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 org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.user.UserSession;
public class BaseContextFactoryTest {
@@ -47,6 +55,15 @@ public class BaseContextFactoryTest {
.setEmail("john@email.com")
.build();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
+
UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
Server server = mock(Server.class);
@@ -55,11 +72,15 @@ public class BaseContextFactoryTest {
BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class);
JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
- BaseContextFactory underTest = new BaseContextFactory(userIdentityAuthenticator, server, jwtHttpHandler);
+ BaseContextFactory underTest = new BaseContextFactory(dbClient, userIdentityAuthenticator, server, jwtHttpHandler, threadLocalUserSession);
@Before
public void setUp() throws Exception {
when(server.getPublicRootUrl()).thenReturn(PUBLIC_ROOT_URL);
+
+ UserDto userDto = dbClient.userDao().insert(dbSession, newUserDto());
+ dbSession.commit();
+ when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider)).thenReturn(userDto);
}
@Test
@@ -80,5 +101,6 @@ public class BaseContextFactoryTest {
context.authenticate(USER_IDENTITY);
verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response));
+ verify(threadLocalUserSession).set(any(UserSession.class));
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/BasicAuthenticatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/BasicAuthenticatorTest.java
new file mode 100644
index 00000000000..692db7ea601
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/BasicAuthenticatorTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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 static com.google.common.base.Charsets.UTF_8;
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.junit.rules.ExpectedException.none;
+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.util.Base64;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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 org.sonar.db.user.UserTesting;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.usertoken.UserTokenAuthenticator;
+
+public class BasicAuthenticatorTest {
+
+ private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
+
+ static final String LOGIN = "login";
+ static final String PASSWORD = "password";
+ static final String CREDENTIALS_IN_BASE64 = toBase64(LOGIN + ":" + PASSWORD);
+
+ static final UserDto USER = UserTesting.newUserDto().setLogin(LOGIN);
+
+ @Rule
+ public ExpectedException expectedException = none();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ CredentialsAuthenticator credentialsAuthenticator = mock(CredentialsAuthenticator.class);
+ UserTokenAuthenticator userTokenAuthenticator = mock(UserTokenAuthenticator.class);
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ BasicAuthenticator underTest = new BasicAuthenticator(dbClient, credentialsAuthenticator, userTokenAuthenticator);
+
+ @Test
+ public void authenticate_from_basic_http_header() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("Basic " + CREDENTIALS_IN_BASE64);
+ when(credentialsAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(USER);
+
+ underTest.authenticate(request);
+
+ verify(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
+ }
+
+ @Test
+ public void authenticate_from_basic_http_header_with_password_containing_semi_colon() throws Exception {
+ String password = "!ascii-only:-)@";
+ when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64(LOGIN + ":" + password));
+ when(credentialsAuthenticator.authenticate(LOGIN, password, request)).thenReturn(USER);
+
+ underTest.authenticate(request);
+
+ verify(credentialsAuthenticator).authenticate(LOGIN, password, request);
+ }
+
+ @Test
+ public void does_not_authenticate_when_no_authorization_header() throws Exception {
+ underTest.authenticate(request);
+
+ verifyZeroInteractions(credentialsAuthenticator);
+ }
+
+ @Test
+ public void does_not_authenticate_when_authorization_header_is_not_BASIC() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("OTHER " + CREDENTIALS_IN_BASE64);
+
+ underTest.authenticate(request);
+
+ verifyZeroInteractions(credentialsAuthenticator);
+ }
+
+ @Test
+ public void fail_to_authenticate_when_no_login() throws Exception {
+ when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64(":" + PASSWORD));
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(request);
+ }
+
+ @Test
+ public void authenticate_from_user_token() throws Exception {
+ insertUser(UserTesting.newUserDto().setLogin(LOGIN));
+ when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of(LOGIN));
+ when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
+
+ Optional<UserDto> userDto = underTest.authenticate(request);
+
+ assertThat(userDto.isPresent()).isTrue();
+ assertThat(userDto.get().getLogin()).isEqualTo(LOGIN);
+ }
+
+ @Test
+ public void does_not_authenticate_from_user_token_when_token_is_invalid() throws Exception {
+ insertUser(UserTesting.newUserDto().setLogin(LOGIN));
+ when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.empty());
+ when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(request);
+ }
+
+ @Test
+ public void does_not_authenticate_from_user_token_when_token_does_not_match_active_user() throws Exception {
+ insertUser(UserTesting.newUserDto().setLogin(LOGIN));
+ when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of("Unknown user"));
+ when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(request);
+ }
+
+ private UserDto insertUser(UserDto userDto){
+ dbClient.userDao().insert(dbSession, userDto);
+ dbSession.commit();
+ return userDto;
+ }
+
+ private static String toBase64(String text){
+ return new String(BASE64_ENCODER.encode(text.getBytes(UTF_8)));
+ }
+
+}
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 2f67f52021a..e276fde6007 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
@@ -54,9 +54,6 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
-import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.user.ServerUserSession;
-import org.sonar.server.user.ThreadLocalUserSession;
public class JwtHttpHandlerTest {
@@ -75,8 +72,6 @@ public class JwtHttpHandlerTest {
@Rule
public DbTester dbTester = DbTester.create(INSTANCE);
- ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
-
DbClient dbClient = dbTester.getDbClient();
DbSession dbSession = dbTester.getSession();
@@ -96,11 +91,10 @@ public class JwtHttpHandlerTest {
UserDto userDto = newUserDto().setLogin(USER_LOGIN);
- JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
+ JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
@Before
public void setUp() throws Exception {
- threadLocalUserSession.remove();
when(system2.now()).thenReturn(NOW);
when(server.isSecured()).thenReturn(true);
when(request.getSession()).thenReturn(httpSession);
@@ -120,7 +114,6 @@ public class JwtHttpHandlerTest {
verify(jwtSerializer).encode(jwtArgumentCaptor.capture());
verifyToken(jwtArgumentCaptor.getValue(), 3 * 24 * 60 * 60, NOW);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
@@ -139,7 +132,7 @@ public class JwtHttpHandlerTest {
int sessionTimeoutInHours = 10;
settings.setProperty("sonar.auth.sessionTimeoutInHours", sessionTimeoutInHours);
- underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
+ underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
underTest.generateToken(userDto, response);
verify(jwtSerializer).encode(jwtArgumentCaptor.capture());
@@ -151,7 +144,7 @@ public class JwtHttpHandlerTest {
int firstSessionTimeoutInHours = 10;
settings.setProperty("sonar.auth.sessionTimeoutInHours", firstSessionTimeoutInHours);
- underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
+ underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
underTest.generateToken(userDto, response);
// The property is updated, but it won't be taking into account
@@ -169,10 +162,9 @@ public class JwtHttpHandlerTest {
Claims claims = createToken(USER_LOGIN, NOW);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isTrue();
verify(jwtSerializer, never()).encode(any(JwtSerializer.JwtSession.class));
- assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
@@ -184,10 +176,9 @@ public class JwtHttpHandlerTest {
claims.put("lastRefreshTime", SIX_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isTrue();
verify(jwtSerializer).refresh(any(Claims.class), eq(3 * 24 * 60 * 60));
- assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
@@ -199,10 +190,9 @@ public class JwtHttpHandlerTest {
claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isTrue();
verify(jwtSerializer, never()).refresh(any(Claims.class), anyInt());
- assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
@@ -215,22 +205,22 @@ public class JwtHttpHandlerTest {
claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
- public void validate_token_fails_with_unauthorized_when_user_is_disabled() throws Exception {
+ public void validate_token_removes_session_when_user_is_disabled() throws Exception {
addJwtCookie();
UserDto user = addUser(false);
Claims claims = createToken(user.getLogin(), NOW);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
- thrown.expect(UnauthorizedException.class);
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
+
+ verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
}
@Test
@@ -239,10 +229,9 @@ public class JwtHttpHandlerTest {
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.empty());
- underTest.validateToken(request, response);
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
@@ -250,7 +239,7 @@ public class JwtHttpHandlerTest {
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
}
@Test
@@ -260,7 +249,7 @@ public class JwtHttpHandlerTest {
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
}
@Test
@@ -287,7 +276,7 @@ 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(response, "CSRF_STATE", 3 * 24 * 60 * 60);
}
@Test
@@ -308,16 +297,6 @@ public class JwtHttpHandlerTest {
verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
verify(jwtCsrfVerifier).removeState(response);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
- }
-
- @Test
- public void remove_token_is_removing_user_session() throws Exception {
- threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
-
- underTest.removeToken(response);
-
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
private void verifyToken(JwtSerializer.JwtSession token, int expectedExpirationTime, long expectedRefreshTime) {
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 9afb606bf41..46dc9582542 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
@@ -25,6 +25,7 @@ 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;
@@ -37,7 +38,13 @@ import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.api.utils.MessageException;
+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 org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.user.UserSession;
public class OAuth2ContextFactoryTest {
@@ -56,6 +63,14 @@ public class OAuth2ContextFactoryTest {
.setEmail("john@email.com")
.build();
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
Server server = mock(Server.class);
OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
@@ -66,12 +81,16 @@ public class OAuth2ContextFactoryTest {
HttpSession session = mock(HttpSession.class);
OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class);
- OAuth2ContextFactory underTest = new OAuth2ContextFactory(userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler);
+ OAuth2ContextFactory underTest = new OAuth2ContextFactory(dbClient, threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler);
@Before
public void setUp() throws Exception {
+ UserDto userDto = dbClient.userDao().insert(dbSession, newUserDto());
+ dbSession.commit();
+
when(request.getSession()).thenReturn(session);
when(identityProvider.getKey()).thenReturn(PROVIDER_KEY);
+ when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider)).thenReturn(userDto);
}
@Test
@@ -133,6 +152,7 @@ public class OAuth2ContextFactoryTest {
verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response));
+ verify(threadLocalUserSession).set(any(UserSession.class));
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
new file mode 100644
index 00000000000..d78b73df527
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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 static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.user.UserTesting.newUserDto;
+
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+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 org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.user.UserSession;
+
+public class UserSessionInitializerTest {
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ ThreadLocalUserSession userSession = mock(ThreadLocalUserSession.class);
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+ BasicAuthenticator basicAuthenticator = mock(BasicAuthenticator.class);
+
+ Settings settings = new Settings();
+
+ UserDto user = newUserDto();
+
+ UserSessionInitializer underTest = new UserSessionInitializer(dbClient, settings, jwtHttpHandler, basicAuthenticator, userSession);
+
+ @Before
+ public void setUp() throws Exception {
+ dbClient.userDao().insert(dbSession, user);
+ dbSession.commit();
+ when(request.getContextPath()).thenReturn("");
+ when(request.getRequestURI()).thenReturn("/measures");
+ }
+
+ @Test
+ public void check_urls() throws Exception {
+ assertPathIsNotIgnored("/");
+ assertPathIsNotIgnored("/foo");
+
+ assertPathIsIgnored("/api/authentication/login");
+ assertPathIsIgnored("/batch/index");
+ assertPathIsIgnored("/batch/file");
+ assertPathIsIgnored("/maintenance/index");
+ assertPathIsIgnored("/setup/index");
+ assertPathIsIgnored("/sessions/new");
+ assertPathIsIgnored("/sessions/logout");
+ assertPathIsIgnored("/api/system/db_migration_status");
+ assertPathIsIgnored("/api/system/status");
+ assertPathIsIgnored("/api/system/migrate_db");
+ assertPathIsIgnored("/api/server/index");
+
+ // exclude static resources
+ assertPathIsIgnored("/css/style.css");
+ assertPathIsIgnored("/fonts/font.ttf");
+ assertPathIsIgnored("/images/logo.png");
+ assertPathIsIgnored("/js/jquery.js");
+ }
+
+ @Test
+ public void validate_session_from_token() throws Exception {
+ when(userSession.isLoggedIn()).thenReturn(true);
+ when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.of(user));
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verify(jwtHttpHandler).validateToken(request, response);
+ verify(response, never()).setStatus(anyInt());
+ }
+
+ @Test
+ public void validate_session_from_basic_authentication() throws Exception {
+ when(userSession.isLoggedIn()).thenReturn(false).thenReturn(true);
+ when(basicAuthenticator.authenticate(request)).thenReturn(Optional.of(user));
+ when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verify(jwtHttpHandler).validateToken(request, response);
+ verify(basicAuthenticator).authenticate(request);
+ verify(userSession).set(any(ServerUserSession.class));
+ verify(response, never()).setStatus(anyInt());
+ }
+
+ @Test
+ public void return_code_401_when_invalid_token_exception() throws Exception {
+ doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_code_401_when_not_authenticated_and_with_force_authentication() throws Exception {
+ when(userSession.isLoggedIn()).thenReturn(false);
+ when(basicAuthenticator.authenticate(request)).thenReturn(Optional.empty());
+ when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
+ settings.setProperty("sonar.forceAuthentication", true);
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_401_and_stop_on_ws() throws Exception {
+ when(request.getRequestURI()).thenReturn("/api/issues");
+ doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+
+ assertThat(underTest.initUserSession(request, response)).isFalse();
+
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_401_and_stop_on_batch_ws() throws Exception {
+ when(request.getRequestURI()).thenReturn("/batch/global");
+ doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
+
+ assertThat(underTest.initUserSession(request, response)).isFalse();
+
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void remove_user_session() throws Exception {
+ underTest.removeUserSession();
+
+ verify(userSession).remove();
+ }
+
+ private void assertPathIsIgnored(String path) {
+ when(request.getRequestURI()).thenReturn(path);
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verifyZeroInteractions(userSession, jwtHttpHandler, basicAuthenticator);
+ reset(userSession, jwtHttpHandler, basicAuthenticator);
+ }
+
+ private void assertPathIsNotIgnored(String path) {
+ when(request.getRequestURI()).thenReturn(path);
+ when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.of(user));
+
+ assertThat(underTest.initUserSession(request, response)).isTrue();
+
+ verify(userSession).set(any(UserSession.class));
+ reset(userSession, jwtHttpHandler, basicAuthenticator);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/ValidateJwtTokenFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/ValidateJwtTokenFilterTest.java
deleted file mode 100644
index 210d940833d..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/ValidateJwtTokenFilterTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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 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 javax.servlet.FilterChain;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.Settings;
-import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.tester.UserSessionRule;
-
-public class ValidateJwtTokenFilterTest {
-
- @Rule
- public UserSessionRule userSession = UserSessionRule.standalone();
-
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- FilterChain chain = mock(FilterChain.class);
-
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
-
- Settings settings = new Settings();
-
- ValidateJwtTokenFilter underTest = new ValidateJwtTokenFilter(settings, jwtHttpHandler, userSession);
-
- @Before
- public void setUp() throws Exception {
- when(request.getContextPath()).thenReturn("");
- when(request.getRequestURI()).thenReturn("/measures");
- }
-
- @Test
- public void do_get_pattern() throws Exception {
- assertThat(underTest.doGetPattern().matches("/")).isTrue();
- assertThat(underTest.doGetPattern().matches("/foo")).isTrue();
-
- assertThat(underTest.doGetPattern().matches("/api/authentication/login")).isFalse();
- assertThat(underTest.doGetPattern().matches("/batch/index")).isFalse();
- assertThat(underTest.doGetPattern().matches("/batch/file")).isFalse();
- assertThat(underTest.doGetPattern().matches("/batch_bootstrap/index")).isFalse();
- assertThat(underTest.doGetPattern().matches("/maintenance/index")).isFalse();
- assertThat(underTest.doGetPattern().matches("/setup/index")).isFalse();
- assertThat(underTest.doGetPattern().matches("/sessions/new")).isFalse();
- assertThat(underTest.doGetPattern().matches("/sessions/logout")).isFalse();
- assertThat(underTest.doGetPattern().matches("/api/system/db_migration_status")).isFalse();
- assertThat(underTest.doGetPattern().matches("/api/system/status")).isFalse();
- assertThat(underTest.doGetPattern().matches("/api/system/status")).isFalse();
- assertThat(underTest.doGetPattern().matches("/api/system/migrate_db")).isFalse();
- assertThat(underTest.doGetPattern().matches("/api/server/index")).isFalse();
-
- // exclude static resources
- assertThat(underTest.doGetPattern().matches("/css/style.css")).isFalse();
- assertThat(underTest.doGetPattern().matches("/fonts/font.ttf")).isFalse();
- assertThat(underTest.doGetPattern().matches("/images/logo.png")).isFalse();
- assertThat(underTest.doGetPattern().matches("/js/jquery.js")).isFalse();
- }
-
- @Test
- public void validate_session() throws Exception {
- userSession.login("john");
- underTest.doFilter(request, response, chain);
-
- verify(jwtHttpHandler).validateToken(request, response);
- verify(chain).doFilter(request, response);
- }
-
- @Test
- public void return_code_401_when_invalid_token_exception() throws Exception {
- userSession.login("john");
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
-
- underTest.doFilter(request, response, chain);
-
- verify(response).setStatus(401);
- verify(chain).doFilter(request, response);
- }
-
- @Test
- public void return_code_401_when_not_authenticated_and_with_force_authentication() throws Exception {
- settings.setProperty("sonar.forceAuthentication", true);
- userSession.anonymous();
-
- underTest.doFilter(request, response, chain);
-
- verify(response).setStatus(401);
- verify(chain).doFilter(request, response);
- }
-
- @Test
- public void return_401_and_stop_on_ws() throws Exception {
- when(request.getRequestURI()).thenReturn("/api/issues");
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
-
- underTest.doFilter(request, response, chain);
-
- verify(response).setStatus(401);
- verifyZeroInteractions(chain);
- }
-
- @Test
- public void return_401_and_stop_on_batch_ws() throws Exception {
- when(request.getRequestURI()).thenReturn("/batch/index");
- doThrow(new UnauthorizedException("invalid token")).when(jwtHttpHandler).validateToken(request, response);
-
- underTest.doFilter(request, response, chain);
-
- verify(response).setStatus(401);
- verifyZeroInteractions(chain);
- }
-
- @Test
- public void ignore_old_batch_ws() throws Exception {
- when(request.getRequestURI()).thenReturn("/batch/name.jar");
-
- underTest.doFilter(request, response, chain);
-
- verify(chain).doFilter(request, response);
- verifyZeroInteractions(jwtHttpHandler, response);
- }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthLoginActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java
index cbd7f126d5f..3f6fd7c8fee 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/authentication/AuthLoginActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/ws/LoginActionTest.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.server.authentication;
+package org.sonar.server.authentication.ws;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doThrow;
@@ -32,17 +32,33 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+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 org.sonar.db.user.UserTesting;
+import org.sonar.server.authentication.CredentialsAuthenticator;
+import org.sonar.server.authentication.JwtHttpHandler;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ThreadLocalUserSession;
-public class AuthLoginActionTest {
+public class LoginActionTest {
static final String LOGIN = "LOGIN";
static final String PASSWORD = "PASSWORD";
- static final UserDto USER = UserTesting.newUserDto().setLogin(LOGIN);
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
@@ -51,7 +67,16 @@ public class AuthLoginActionTest {
CredentialsAuthenticator credentialsAuthenticator = mock(CredentialsAuthenticator.class);
JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
- AuthLoginAction underTest = new AuthLoginAction(credentialsAuthenticator, jwtHttpHandler);
+ UserDto user = UserTesting.newUserDto().setLogin(LOGIN);
+
+ LoginAction underTest = new LoginAction(dbClient, credentialsAuthenticator, jwtHttpHandler, threadLocalUserSession);
+
+ @Before
+ public void setUp() throws Exception {
+ threadLocalUserSession.remove();
+ dbClient.userDao().insert(dbSession, user);
+ dbSession.commit();
+ }
@Test
public void do_get_pattern() throws Exception {
@@ -62,12 +87,13 @@ public class AuthLoginActionTest {
@Test
public void do_authenticate() throws Exception {
- when(credentialsAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(USER);
+ when(credentialsAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(user);
executeRequest(LOGIN, PASSWORD);
+ assertThat(threadLocalUserSession.isLoggedIn()).isTrue();
verify(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
- verify(jwtHttpHandler).generateToken(USER, response);
+ verify(jwtHttpHandler).generateToken(user, response);
verifyZeroInteractions(chain);
}
@@ -87,6 +113,7 @@ public class AuthLoginActionTest {
executeRequest(LOGIN, PASSWORD);
verify(response).setStatus(401);
+ assertThat(threadLocalUserSession.isLoggedIn()).isFalse();
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
index 06a8be8d1fc..1e75fe2279a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionFilterTest.java
@@ -19,66 +19,76 @@
*/
package org.sonar.server.user;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+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.FilterConfig;
import javax.servlet.ServletException;
-import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
-import org.junit.After;
+import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.sonar.core.platform.ComponentContainer;
+import org.sonar.server.authentication.UserSessionInitializer;
import org.sonar.server.platform.Platform;
-import org.sonar.server.tester.MockUserSession;
-
-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 UserSessionFilterTest {
- private ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
- private Platform platform = mock(Platform.class);
- private ComponentContainer componentContainer = mock(ComponentContainer.class);
- private HttpServletRequest httpRequest = mock(HttpServletRequest.class);
- private ServletResponse httpResponse = mock(ServletResponse.class);
- private FilterChain chain = mock(FilterChain.class);
+
+ UserSessionInitializer userSessionInitializer = mock(UserSessionInitializer.class);
+ Platform platform = mock(Platform.class);
+ ComponentContainer componentContainer = mock(ComponentContainer.class);
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+ FilterChain chain = mock(FilterChain.class);
+
+ UserSessionFilter underTest = new UserSessionFilter(platform);
@Before
public void setUp() {
when(platform.getContainer()).thenReturn(componentContainer);
- // for test isolation
- threadLocalUserSession.remove();
}
- @After
- public void tearDown() {
- threadLocalUserSession.remove();
+ @Test
+ public void cleanup_user_session_after_request_handling() throws IOException, ServletException {
+ when(componentContainer.getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
+ when(userSessionInitializer.initUserSession(request, response)).thenReturn(true);
+
+ underTest.doFilter(request, response, chain);
+
+ verify(chain).doFilter(request, response);
+ verify(userSessionInitializer).initUserSession(request, response);
+ verify(userSessionInitializer).removeUserSession();
}
@Test
- public void should_cleanup_user_session_after_request_handling() throws IOException, ServletException {
- when(componentContainer.getComponentByType(ThreadLocalUserSession.class)).thenReturn(threadLocalUserSession);
+ public void stop_when_user_session_return_false() throws Exception {
+ when(componentContainer.getComponentByType(UserSessionInitializer.class)).thenReturn(userSessionInitializer);
+ when(userSessionInitializer.initUserSession(request, response)).thenReturn(false);
- threadLocalUserSession.set(new MockUserSession("karadoc").setUserId(123));
- assertThat(threadLocalUserSession.hasSession()).isTrue();
- UserSessionFilter filter = new UserSessionFilter(platform);
- filter.doFilter(httpRequest, httpResponse, chain);
+ underTest.doFilter(request, response, chain);
- verify(chain).doFilter(httpRequest, httpResponse);
- assertThat(threadLocalUserSession.hasSession()).isFalse();
+ verify(chain, never()).doFilter(request, response);
+ verify(userSessionInitializer).initUserSession(request, response);
+ verify(userSessionInitializer).removeUserSession();
}
@Test
- public void does_not_fail_if_container_has_no_ThreadLocalUserSession() throws Exception {
- UserSessionFilter filter = new UserSessionFilter(platform);
- filter.doFilter(httpRequest, httpResponse, chain);
+ public void does_nothing_when_not_initialized() throws Exception {
+ underTest.doFilter(request, response, chain);
+
+ verify(chain).doFilter(request, response);
+ verifyZeroInteractions(userSessionInitializer);
}
@Test
public void just_for_fun_and_coverage() throws ServletException {
- UserSessionFilter filter = new UserSessionFilter(platform);
+ UserSessionFilter filter = new UserSessionFilter();
filter.init(mock(FilterConfig.class));
filter.destroy();
// do not fail
diff --git a/server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java
index 74de38b0037..e0b4516aba4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java
@@ -19,7 +19,12 @@
*/
package org.sonar.server.usertoken;
-import com.google.common.base.Optional;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.user.UserTokenTesting.newUserToken;
+
+import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -28,12 +33,6 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
-
-
public class UserTokenAuthenticatorTest {
static final String GRACE_HOPPER = "grace.hopper";
static final String ADA_LOVELACE = "ada.lovelace";
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb b/server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb
index 02f70471036..c9be7f12c8a 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/lib/authenticated_system.rb
@@ -8,7 +8,7 @@ module AuthenticatedSystem
# Accesses the current user from the session.
# Future calls avoid the database because nil is not equal to false.
def current_user
- @current_user ||= (login_from_java_user_session || login_from_basic_auth) unless @current_user == false
+ @current_user ||= login_from_java_user_session unless @current_user == false
end
# Store the given user
@@ -124,31 +124,6 @@ module AuthenticatedSystem
self.current_user = User.find_by_id(user_id) if user_id
end
- # Called from #current_user. Now, attempt to login by basic authentication information.
- def login_from_basic_auth
- authenticate_with_http_basic do |login, password|
- # The access token is sent as the login of Basic authentication. To distinguish with regular logins,
- # the convention is that the password is empty
- if password.empty? && login.present?
- # authentication by access token
- token_authenticator = Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerUsertoken::UserTokenAuthenticator.java_class)
- authenticated_login = token_authenticator.authenticate(login)
- if authenticated_login.isPresent()
- user = User.find_active_by_login(authenticated_login.get())
- if user
- user.token_authenticated=true
- result = user
- end
- end
- else
- # regular Basic authentication with login and password
- result = User.authenticate(login, password, servlet_request)
- end
- raise Errors::AccessDenied unless login.blank? || result
- self.current_user = result
- end
- end
-
#
# Logout
#