+++ /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 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 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.web.ServletFilter;
-import org.sonar.db.user.UserDto;
-import org.sonar.server.exceptions.UnauthorizedException;
-
-public class AuthLoginAction extends ServletFilter {
-
- static final String AUTH_LOGIN_URL = "/api/authentication/login";
-
- private static final String POST = "POST";
-
- private final CredentialsAuthenticator credentialsAuthenticator;
- private final JwtHttpHandler jwtHttpHandler;
-
- public AuthLoginAction(CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler) {
- this.credentialsAuthenticator = credentialsAuthenticator;
- this.jwtHttpHandler = jwtHttpHandler;
- }
-
- @Override
- public UrlPattern doGetPattern() {
- return UrlPattern.create(AUTH_LOGIN_URL);
- }
-
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) servletRequest;
- HttpServletResponse response = (HttpServletResponse) servletResponse;
-
- if (!request.getMethod().equals(POST)) {
- response.setStatus(HTTP_BAD_REQUEST);
- return;
- }
- try {
- UserDto userDto = authenticate(request);
- jwtHttpHandler.generateToken(userDto, response);
- // 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());
- }
- }
-
- private UserDto authenticate(HttpServletRequest request) {
- String login = request.getParameter("login");
- String password = request.getParameter("password");
- if (isNullOrEmpty(login) || isNullOrEmpty(password)) {
- throw new UnauthorizedException();
- }
- return credentialsAuthenticator.authenticate(login, password, request);
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // Nothing to do
- }
-
- @Override
- public void destroy() {
- // Nothing to do
- }
-}
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
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);
}
}
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) {
public void authenticate(UserIdentity userIdentity) {
UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
jwtHttpHandler.generateToken(userDto, response);
+ threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
}
}
--- /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 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);
+ }
+ }
+
+}
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;
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 {
// 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(
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) {
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));
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) {
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) {
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;
public void authenticate(UserIdentity userIdentity) {
UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
jwtHttpHandler.generateToken(userDto, response);
+ threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
}
}
--- /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 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.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.util.Optional;
+import java.util.Set;
+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.db.DbClient;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ThreadLocalUserSession;
+
+@ServerSide
+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",
+ "/maintenance/*",
+ "/setup/*",
+ "/sessions/*",
+ "/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
+ "/api/server/*",
+ 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 BasicAuthenticator basicAuthenticator;
+ private final ThreadLocalUserSession 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;
+ }
+
+ public boolean initUserSession(HttpServletRequest request, HttpServletResponse response) {
+ String path = request.getRequestURI().replaceFirst(request.getContextPath(), "");
+ try {
+ // Do not set user session when url is excluded
+ if (!URL_PATTERN.matches(path)) {
+ return true;
+ }
+ setUserSession(request, response);
+ return true;
+ } catch (UnauthorizedException e) {
+ jwtHttpHandler.removeToken(response);
+ response.setStatus(HTTP_UNAUTHORIZED);
+ if (isWsUrl(path)) {
+ return false;
+ }
+ // WS should stop here. Rails page should continue in order to deal with redirection
+ return true;
+ }
+ }
+
+ 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));
+ }
+ }
+
+ public void removeUserSession() {
+ userSession.remove();
+ }
+
+ // 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);
+ }
+
+ private static boolean isWsUrl(String path) {
+ return path.startsWith("/batch/") || path.startsWith("/api/");
+ }
+
+}
+++ /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 static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
-import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
-import static org.sonar.server.authentication.AuthLoginAction.AUTH_LOGIN_URL;
-
-import com.google.common.collect.ImmutableSet;
-import java.io.IOException;
-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.server.exceptions.UnauthorizedException;
-import org.sonar.server.user.UserSession;
-
-@ServerSide
-public class ValidateJwtTokenFilter extends ServletFilter {
-
- // 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",
- "/maintenance/*",
- "/setup/*",
- "/sessions/*",
- "/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
- "/api/server/*",
- AUTH_LOGIN_URL
- );
-
- private final Settings settings;
- private final JwtHttpHandler jwtHttpHandler;
- private final UserSession userSession;
-
- public ValidateJwtTokenFilter(Settings settings, JwtHttpHandler jwtHttpHandler, UserSession userSession) {
- this.settings = settings;
- this.jwtHttpHandler = jwtHttpHandler;
- 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;
- String path = request.getRequestURI().replaceFirst(request.getContextPath(), "");
-
- try {
- if (isDeprecatedBatchWs(path)) {
- chain.doFilter(request, response);
- return;
- }
-
- 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);
- } catch (UnauthorizedException e) {
- jwtHttpHandler.removeToken(response);
- response.setStatus(HTTP_UNAUTHORIZED);
-
- if (isWsUrl(path)) {
- return;
- }
- // WS should stop here. Rails page should continue in order to deal with redirection
- chain.doFilter(request, response);
- }
- }
-
- // Scanner is still using deprecated /batch/<File name>.jar WS
- private static boolean isDeprecatedBatchWs(String path){
- return path.startsWith("/batch/") && path.endsWith(".jar");
- }
-
- private static boolean isWsUrl(String path){
- return path.startsWith("/batch/") || path.startsWith("/api/");
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // Nothing to do
- }
-
- @Override
- public void destroy() {
- // Nothing to do
- }
-}
--- /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.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 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.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 LoginAction extends ServletFilter {
+
+ 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 LoginAction(DbClient dbClient, CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler, ThreadLocalUserSession threadLocalUserSession) {
+ this.dbClient = dbClient;
+ this.credentialsAuthenticator = credentialsAuthenticator;
+ this.jwtHttpHandler = jwtHttpHandler;
+ this.threadLocalUserSession = threadLocalUserSession;
+ }
+
+ @Override
+ public UrlPattern doGetPattern() {
+ return UrlPattern.create(AUTH_LOGIN_URL);
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ if (!request.getMethod().equals(POST)) {
+ response.setStatus(HTTP_BAD_REQUEST);
+ return;
+ }
+ 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());
+ }
+ }
+
+ private UserDto authenticate(HttpServletRequest request) {
+ String login = request.getParameter("login");
+ String password = request.getParameter("password");
+ if (isNullOrEmpty(login) || isNullOrEmpty(password)) {
+ throw new UnauthorizedException();
+ }
+ return credentialsAuthenticator.authenticate(login, password, request);
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // Nothing to do
+ }
+
+ @Override
+ public void destroy() {
+ // Nothing to do
+ }
+}
*/
package org.sonar.server.user;
+import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
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
}
}
* 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);
}
+++ /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 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 javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.junit.Test;
-import org.sonar.db.user.UserDto;
-import org.sonar.db.user.UserTesting;
-import org.sonar.server.exceptions.UnauthorizedException;
-
-public class AuthLoginActionTest {
-
- static final String LOGIN = "LOGIN";
- static final String PASSWORD = "PASSWORD";
-
- static final UserDto USER = UserTesting.newUserDto().setLogin(LOGIN);
-
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- FilterChain chain = mock(FilterChain.class);
-
- CredentialsAuthenticator credentialsAuthenticator = mock(CredentialsAuthenticator.class);
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
-
- AuthLoginAction underTest = new AuthLoginAction(credentialsAuthenticator, jwtHttpHandler);
-
- @Test
- public void do_get_pattern() throws Exception {
- assertThat(underTest.doGetPattern().matches("/api/authentication/login")).isTrue();
- assertThat(underTest.doGetPattern().matches("/api/authentication/logout")).isFalse();
- assertThat(underTest.doGetPattern().matches("/foo")).isFalse();
- }
-
- @Test
- public void do_authenticate() throws Exception {
- when(credentialsAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(USER);
-
- executeRequest(LOGIN, PASSWORD);
-
- verify(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
- verify(jwtHttpHandler).generateToken(USER, response);
- verifyZeroInteractions(chain);
- }
-
- @Test
- public void ignore_get_request() throws Exception {
- when(request.getMethod()).thenReturn("GET");
-
- underTest.doFilter(request, response, chain);
-
- verifyZeroInteractions(credentialsAuthenticator, jwtHttpHandler, chain);
- }
-
- @Test
- public void return_authorized_code_when_unauthorized_exception_is_thrown() throws Exception {
- doThrow(new UnauthorizedException()).when(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
-
- executeRequest(LOGIN, PASSWORD);
-
- verify(response).setStatus(401);
- }
-
- @Test
- public void return_unauthorized_code_when_no_login() throws Exception {
- executeRequest(null, PASSWORD);
- verify(response).setStatus(401);
- }
-
- @Test
- public void return_unauthorized_code_when_empty_login() throws Exception {
- executeRequest("", PASSWORD);
- verify(response).setStatus(401);
- }
-
- @Test
- public void return_unauthorized_code_when_no_password() throws Exception {
- executeRequest(LOGIN, null);
- verify(response).setStatus(401);
- }
-
- @Test
- public void return_unauthorized_code_when_empty_password() throws Exception {
- executeRequest(LOGIN, "");
- verify(response).setStatus(401);
- }
-
- private void executeRequest(String login, String password) throws IOException, ServletException {
- when(request.getMethod()).thenReturn("POST");
- when(request.getParameter("login")).thenReturn(login);
- when(request.getParameter("password")).thenReturn(password);
- underTest.doFilter(request, response, chain);
- }
-}
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 {
.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);
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
context.authenticate(USER_IDENTITY);
verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
verify(jwtHttpHandler).generateToken(any(UserDto.class), 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 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)));
+ }
+
+}
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 {
@Rule
public DbTester dbTester = DbTester.create(INSTANCE);
- ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
-
DbClient dbClient = dbTester.getDbClient();
DbSession dbSession = dbTester.getSession();
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);
verify(jwtSerializer).encode(jwtArgumentCaptor.capture());
verifyToken(jwtArgumentCaptor.getValue(), 3 * 24 * 60 * 60, NOW);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
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());
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
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
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
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
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
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
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
}
@Test
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
- assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
+ assertThat(underTest.validateToken(request, response).isPresent()).isFalse();
}
@Test
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
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) {
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 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 {
.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);
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
verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response));
+ verify(threadLocalUserSession).set(any(UserSession.class));
}
@Test
--- /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 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);
+ }
+}
+++ /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 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);
- }
-}
--- /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.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 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 LoginActionTest {
+
+ static final String LOGIN = "LOGIN";
+ static final String PASSWORD = "PASSWORD";
+
+ @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);
+ FilterChain chain = mock(FilterChain.class);
+
+ CredentialsAuthenticator credentialsAuthenticator = mock(CredentialsAuthenticator.class);
+ JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+
+ 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 {
+ assertThat(underTest.doGetPattern().matches("/api/authentication/login")).isTrue();
+ assertThat(underTest.doGetPattern().matches("/api/authentication/logout")).isFalse();
+ assertThat(underTest.doGetPattern().matches("/foo")).isFalse();
+ }
+
+ @Test
+ public void do_authenticate() throws Exception {
+ 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);
+ verifyZeroInteractions(chain);
+ }
+
+ @Test
+ public void ignore_get_request() throws Exception {
+ when(request.getMethod()).thenReturn("GET");
+
+ underTest.doFilter(request, response, chain);
+
+ verifyZeroInteractions(credentialsAuthenticator, jwtHttpHandler, chain);
+ }
+
+ @Test
+ public void return_authorized_code_when_unauthorized_exception_is_thrown() throws Exception {
+ doThrow(new UnauthorizedException()).when(credentialsAuthenticator).authenticate(LOGIN, PASSWORD, request);
+
+ executeRequest(LOGIN, PASSWORD);
+
+ verify(response).setStatus(401);
+ assertThat(threadLocalUserSession.isLoggedIn()).isFalse();
+ }
+
+ @Test
+ public void return_unauthorized_code_when_no_login() throws Exception {
+ executeRequest(null, PASSWORD);
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_unauthorized_code_when_empty_login() throws Exception {
+ executeRequest("", PASSWORD);
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_unauthorized_code_when_no_password() throws Exception {
+ executeRequest(LOGIN, null);
+ verify(response).setStatus(401);
+ }
+
+ @Test
+ public void return_unauthorized_code_when_empty_password() throws Exception {
+ executeRequest(LOGIN, "");
+ verify(response).setStatus(401);
+ }
+
+ private void executeRequest(String login, String password) throws IOException, ServletException {
+ when(request.getMethod()).thenReturn("POST");
+ when(request.getParameter("login")).thenReturn(login);
+ when(request.getParameter("password")).thenReturn(password);
+ underTest.doFilter(request, response, chain);
+ }
+}
*/
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
*/
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;
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";
# 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
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
#