private void checkExistence(String username) {
if (!data.containsKey(username + ".password")) {
- throw new RuntimeException("No such user");
+ throw new IllegalArgumentException("No such user : " + username);
}
}
*/
package it.user;
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static util.ItUtils.pluginArtifact;
+import static util.ItUtils.setServerProperty;
+
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.sonar.orchestrator.Orchestrator;
import util.QaOnly;
import util.selenium.SeleneseTest;
-import static java.net.HttpURLConnection.HTTP_OK;
-import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static util.ItUtils.pluginArtifact;
-import static util.ItUtils.setServerProperty;
-
/**
* Test deprecated authentication done by Rails. It's kept has every features has not bee migrated to java yet.
*
--- /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
+ }
+}
OAuth2ContextFactory.class,
UserIdentityAuthenticator.class,
OAuthCsrfVerifier.class,
- GenerateJwtTokenFilter.class,
ValidateJwtTokenFilter.class,
JwtSerializer.class,
JwtHttpHandler.class,
- JwtCsrfVerifier.class);
+ JwtCsrfVerifier.class,
+ AuthLoginAction.class,
+ CredentialsAuthenticator.class,
+ RealmAuthenticator.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.user.UserDto;
public class BaseContextFactory {
private final UserIdentityAuthenticator userIdentityAuthenticator;
private final Server server;
+ private final JwtHttpHandler jwtHttpHandler;
- public BaseContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server) {
+ public BaseContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, JwtHttpHandler jwtHttpHandler) {
this.userIdentityAuthenticator = userIdentityAuthenticator;
this.server = server;
+ this.jwtHttpHandler = jwtHttpHandler;
}
public BaseIdentityProvider.Context newContext(HttpServletRequest request, HttpServletResponse response, BaseIdentityProvider identityProvider) {
@Override
public void authenticate(UserIdentity userIdentity) {
- userIdentityAuthenticator.authenticate(userIdentity, identityProvider, request, response);
+ UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
+ jwtHttpHandler.generateToken(userDto, 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;
+
+import static org.sonar.db.user.UserDto.encryptPassword;
+
+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;
+
+public class CredentialsAuthenticator {
+
+ private final DbClient dbClient;
+ private final RealmAuthenticator externalAuthenticator;
+
+ public CredentialsAuthenticator(DbClient dbClient, RealmAuthenticator externalAuthenticator) {
+ this.dbClient = dbClient;
+ this.externalAuthenticator = externalAuthenticator;
+ }
+
+ public UserDto authenticate(String userLogin, String userPassword, HttpServletRequest request) {
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ return authenticate(dbSession, userLogin, userPassword, request);
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private UserDto authenticate(DbSession dbSession, String userLogin, String userPassword, HttpServletRequest request) {
+ UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin);
+ if (user != null && user.isLocal()) {
+ return authenticateFromDb(user, userPassword);
+ }
+ Optional<UserDto> userDto = externalAuthenticator.authenticate(userLogin, userPassword, request);
+ if (userDto.isPresent()) {
+ return userDto.get();
+ }
+ throw new UnauthorizedException();
+ }
+
+ private static UserDto authenticateFromDb(UserDto userDto, String userPassword) {
+ String cryptedPassword = userDto.getCryptedPassword();
+ String salt = userDto.getSalt();
+ if (cryptedPassword == null || salt == null
+ || !cryptedPassword.equals(encryptPassword(userPassword, salt))) {
+ throw new UnauthorizedException();
+ }
+ return 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 java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.PrintWriter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.WriteListener;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpServletResponseWrapper;
-import org.sonar.api.web.ServletFilter;
-import org.sonar.server.user.UserSession;
-
-/**
- * This filter creates web session when user authenticates on URL "/sessions/login".
- * The generated session is server stateless.
- */
-public class GenerateJwtTokenFilter extends ServletFilter {
-
- private static final String POST = "POST";
-
- private final JwtHttpHandler jwtHttpHandler;
- private final UserSession userSession;
-
- public GenerateJwtTokenFilter(JwtHttpHandler jwtHttpHandler, UserSession userSession) {
- this.jwtHttpHandler = jwtHttpHandler;
- this.userSession = userSession;
- }
-
- @Override
- public UrlPattern doGetPattern() {
- return UrlPattern.create("/sessions/login");
- }
-
- @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)) {
- BufferResponseWrapper wrapper = new BufferResponseWrapper(response);
- chain.doFilter(request, wrapper);
- if (userSession.isLoggedIn()) {
- jwtHttpHandler.generateToken(userSession.getLogin(), response);
- }
- response.getOutputStream().write(wrapper.getWrapperBytes());
- } else {
- chain.doFilter(request, response);
- }
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // Nothing to do
- }
-
- @Override
- public void destroy() {
- // Nothing to do
- }
-
- /**
- * As the RackFilter is executed before this filter, the reponse is commited and it's not possible anymore to add cookie.
- * So we're create a buffer response wrapper that will buffer the dat that should be send to the browser in order to not commit the response.
- * It's then possible to add cookie before flushing data to the browser.
- *
- * See <a href="http://stackoverflow.com/questions/11025605/response-is-committing-and-dofilter-chain-is-broken">
- *
- * Note : this must be removed when authentication will not use rails anymore
- */
- private static final class BufferResponseWrapper extends HttpServletResponseWrapper {
-
- private BufferServletOutputStream stream = new BufferServletOutputStream();
-
- BufferResponseWrapper(HttpServletResponse httpServletResponse) {
- super(httpServletResponse);
- }
-
- @Override
- public ServletOutputStream getOutputStream() throws IOException {
- return stream;
- }
-
- @Override
- public PrintWriter getWriter() throws IOException {
- return new PrintWriter(stream);
- }
-
- byte[] getWrapperBytes() {
- return stream.getBytes();
- }
- }
-
- private static final class BufferServletOutputStream extends ServletOutputStream {
- private ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- @Override
- public void write(int b) throws IOException {
- out.write(b);
- }
-
- byte[] getBytes() {
- return out.toByteArray();
- }
-
- @Override
- public boolean isReady() {
- return false;
- }
-
- @Override
- public void setWriteListener(WriteListener listener) {
- // Nothing to do
- }
- }
-}
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 {
// The value must be lower than sessionTimeoutInSeconds
private static final int SESSION_REFRESH_IN_SECONDS = 5 * 60;
- private static final String RAILS_USER_ID_SESSION = "user_id";
-
private final System2 system2;
private final DbClient dbClient;
private final Server server;
// 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) {
+ public JwtHttpHandler(System2 system2, DbClient dbClient, Server server, Settings settings, JwtSerializer jwtSerializer, JwtCsrfVerifier jwtCsrfVerifier,
+ ThreadLocalUserSession threadLocalUserSession) {
this.jwtSerializer = jwtSerializer;
this.server = server;
this.dbClient = dbClient;
this.system2 = system2;
this.sessionTimeoutInSeconds = getSessionTimeoutInSeconds(settings);
this.jwtCsrfVerifier = jwtCsrfVerifier;
+ this.threadLocalUserSession = threadLocalUserSession;
}
- void generateToken(String userLogin, HttpServletResponse response) {
+ void generateToken(UserDto user, HttpServletResponse response) {
String csrfState = jwtCsrfVerifier.generateState(response, sessionTimeoutInSeconds);
String token = jwtSerializer.encode(new JwtSerializer.JwtSession(
- userLogin,
+ user.getLogin(),
sessionTimeoutInSeconds,
ImmutableMap.of(
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));
+ }
+ }
+
+ private void validate(HttpServletRequest request, HttpServletResponse response) {
+ Optional<String> token = getTokenFromCookie(request);
+ if (!token.isPresent()) {
+ return;
+ }
+ validateToken(token.get(), request, response);
+ }
+
+ private static Optional<String> getTokenFromCookie(HttpServletRequest request) {
Optional<Cookie> jwtCookie = findCookie(JWT_COOKIE, request);
- if (jwtCookie.isPresent()) {
- Cookie cookie = jwtCookie.get();
- String token = cookie.getValue();
- if (!isNullOrEmpty(token)) {
- validateToken(token, request, response);
- }
+ if (!jwtCookie.isPresent()) {
+ return Optional.empty();
+ }
+ Cookie cookie = jwtCookie.get();
+ String token = cookie.getValue();
+ if (isNullOrEmpty(token)) {
+ return Optional.empty();
}
+ return Optional.of(token);
}
private void validateToken(String tokenEncoded, HttpServletRequest request, HttpServletResponse response) {
Optional<Claims> claims = jwtSerializer.decode(tokenEncoded);
if (!claims.isPresent()) {
- removeSession(request, response);
+ removeToken(response);
return;
}
Date now = new Date(system2.now());
-
Claims token = claims.get();
if (now.after(DateUtils.addSeconds(token.getIssuedAt(), SESSION_DISCONNECT_IN_SECONDS))) {
- removeSession(request, response);
+ removeToken(response);
return;
}
-
- Optional<UserDto> user = selectUserFromDb(token.getSubject());
- if (!user.isPresent()) {
- removeSession(request, response);
- return;
- }
-
jwtCsrfVerifier.verifyState(request, (String) token.get(CSRF_JWT_PARAM));
- request.getSession().setAttribute(RAILS_USER_ID_SESSION, user.get().getId());
+
if (now.after(DateUtils.addSeconds(getLastRefreshDate(token), SESSION_REFRESH_IN_SECONDS))) {
refreshToken(token, response);
}
+
+ Optional<UserDto> user = selectUserFromDb(token.getSubject());
+ if (!user.isPresent()) {
+ removeToken(response);
+ throw new UnauthorizedException("User does not exist");
+ }
+ threadLocalUserSession.set(createForUser(dbClient, user.get()));
}
private static Date getLastRefreshDate(Claims token) {
jwtCsrfVerifier.refreshState(response, (String) token.get(CSRF_JWT_PARAM), sessionTimeoutInSeconds);
}
- private void removeSession(HttpServletRequest request, HttpServletResponse response) {
- request.getSession().removeAttribute(RAILS_USER_ID_SESSION);
+ 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.user.UserDto;
public class OAuth2ContextFactory {
private final UserIdentityAuthenticator userIdentityAuthenticator;
private final Server server;
private final OAuthCsrfVerifier csrfVerifier;
+ private final JwtHttpHandler jwtHttpHandler;
- public OAuth2ContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, OAuthCsrfVerifier csrfVerifier) {
+ public OAuth2ContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler) {
this.userIdentityAuthenticator = userIdentityAuthenticator;
this.server = server;
this.csrfVerifier = csrfVerifier;
+ this.jwtHttpHandler = jwtHttpHandler;
}
public OAuth2IdentityProvider.InitContext newContext(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider identityProvider) {
@Override
public void authenticate(UserIdentity userIdentity) {
- userIdentityAuthenticator.authenticate(userIdentity, identityProvider, request, response);
+ UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
+ jwtHttpHandler.generateToken(userDto, 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;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang.StringUtils.trimToNull;
+import static org.elasticsearch.common.Strings.isNullOrEmpty;
+import static org.sonar.api.CoreProperties.CORE_AUTHENTICATOR_CREATE_USERS;
+import static org.sonar.server.user.UserUpdater.SQ_AUTHORITY;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import org.sonar.api.Startable;
+import org.sonar.api.config.Settings;
+import org.sonar.api.security.Authenticator;
+import org.sonar.api.security.ExternalGroupsProvider;
+import org.sonar.api.security.ExternalUsersProvider;
+import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.security.UserDetails;
+import org.sonar.api.server.authentication.Display;
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.SecurityRealmFactory;
+
+public class RealmAuthenticator implements Startable {
+
+ private static final Logger LOG = Loggers.get(RealmAuthenticator.class);
+
+ private final Settings settings;
+ private final SecurityRealmFactory securityRealmFactory;
+ private final UserIdentityAuthenticator userIdentityAuthenticator;
+
+ private SecurityRealm realm;
+ private Authenticator authenticator;
+ private ExternalUsersProvider externalUsersProvider;
+ private ExternalGroupsProvider externalGroupsProvider;
+
+ public RealmAuthenticator(Settings settings, SecurityRealmFactory securityRealmFactory, UserIdentityAuthenticator userIdentityAuthenticator) {
+ this.settings = settings;
+ this.securityRealmFactory = securityRealmFactory;
+ this.userIdentityAuthenticator = userIdentityAuthenticator;
+ }
+
+ @Override
+ public void start() {
+ realm = securityRealmFactory.getRealm();
+ if (realm != null) {
+ authenticator = requireNonNull(realm.doGetAuthenticator(), "No authenticator available");
+ externalUsersProvider = requireNonNull(realm.getUsersProvider(), "No users provider available");
+ externalGroupsProvider = realm.getGroupsProvider();
+ }
+ }
+
+ public Optional<UserDto> authenticate(String userLogin, String userPassword, HttpServletRequest request) {
+ if (realm == null) {
+ return Optional.empty();
+ }
+ return Optional.of(doAuthenticate(getLogin(userLogin), userPassword, request));
+ }
+
+ private UserDto doAuthenticate(String userLogin, String userPassword, HttpServletRequest request) {
+ try {
+ ExternalUsersProvider.Context externalUsersProviderContext = new ExternalUsersProvider.Context(userLogin, request);
+ UserDetails details = externalUsersProvider.doGetUserDetails(externalUsersProviderContext);
+ if (details == null) {
+ throw new UnauthorizedException("No user details");
+ }
+ Authenticator.Context authenticatorContext = new Authenticator.Context(userLogin, userPassword, request);
+ boolean status = authenticator.doAuthenticate(authenticatorContext);
+ if (!status) {
+ throw new UnauthorizedException("Fail to authenticate from external provider");
+ }
+ return synchronize(userLogin, details, request);
+ } catch (Exception e) {
+ // It seems that with Realm API it's expected to log the error and to not authenticate the user
+ LOG.error("Error during authentication", e);
+ throw new UnauthorizedException();
+ }
+ }
+
+ private UserDto synchronize(String userLogin, UserDetails details, HttpServletRequest request) {
+ String name = details.getName();
+ UserIdentity.Builder userIdentityBuilder = UserIdentity.builder()
+ .setLogin(userLogin)
+ .setName(isNullOrEmpty(name) ? userLogin : name)
+ .setEmail(trimToNull(details.getEmail()))
+ .setProviderLogin(userLogin);
+ if (externalGroupsProvider != null) {
+ ExternalGroupsProvider.Context context = new ExternalGroupsProvider.Context(userLogin, request);
+ Collection<String> groups = externalGroupsProvider.doGetGroups(context);
+ userIdentityBuilder.setGroups(new HashSet<>(groups));
+ }
+ return userIdentityAuthenticator.authenticate(userIdentityBuilder.build(), new ExternalIdentityProvider());
+ }
+
+ private String getLogin(String userLogin) {
+ if (settings.getBoolean("sonar.authenticator.downcase")) {
+ return userLogin.toLowerCase(Locale.ENGLISH);
+ }
+ return userLogin;
+ }
+
+ private class ExternalIdentityProvider implements IdentityProvider {
+ @Override
+ public String getKey() {
+ return SQ_AUTHORITY;
+ }
+
+ @Override
+ public String getName() {
+ return SQ_AUTHORITY;
+ }
+
+ @Override
+ public Display getDisplay() {
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean allowsUsersToSignUp() {
+ return settings.getBoolean(CORE_AUTHENTICATOR_CREATE_USERS);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // Nothing to do
+ }
+}
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.sonar.api.server.authentication.IdentityProvider;
import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.api.server.authentication.UserIdentity;
private final DbClient dbClient;
private final UserUpdater userUpdater;
- private final JwtHttpHandler jwtHttpHandler;
- public UserIdentityAuthenticator(DbClient dbClient, UserUpdater userUpdater, JwtHttpHandler jwtHttpHandler) {
+ public UserIdentityAuthenticator(DbClient dbClient, UserUpdater userUpdater) {
this.dbClient = dbClient;
this.userUpdater = userUpdater;
- this.jwtHttpHandler = jwtHttpHandler;
}
- public void authenticate(UserIdentity user, IdentityProvider provider, HttpServletRequest request, HttpServletResponse response) {
- UserDto userDb = register(user, provider);
-
- // hack to disable Ruby on Rails authentication
- request.getSession().setAttribute("user_id", userDb.getId());
- jwtHttpHandler.generateToken(userDb.getLogin(), response);
+ public UserDto authenticate(UserIdentity user, IdentityProvider provider) {
+ return register(user, provider);
}
private UserDto register(UserIdentity user, IdentityProvider provider) {
.setLogin(userLogin)
.setEmail(user.getEmail())
.setName(user.getName())
- .setExternalIdentity(new ExternalIdentity(provider.getKey(), user.getProviderLogin()))
- );
+ .setExternalIdentity(new ExternalIdentity(provider.getKey(), user.getProviderLogin())));
UserDto newUser = dbClient.userDao().selectOrFailByLogin(dbSession, userLogin);
syncGroups(dbSession, user, newUser);
return newUser;
}
private void addGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToAdd, Map<String, GroupDto> groupsByName) {
- if (!groupsToAdd.isEmpty()) {
- for (String groupToAdd : groupsToAdd) {
- GroupDto groupDto = groupsByName.get(groupToAdd);
- if (groupDto != null) {
- LOGGER.debug("Adding group '{}' to user '{}'", groupDto.getName(), userDto.getLogin());
- dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
- }
- }
- }
+ groupsToAdd.stream().map(groupsByName::get).filter(groupDto -> groupDto != null).forEach(
+ groupDto -> {
+ LOGGER.debug("Adding group '{}' to user '{}'", groupDto.getName(), userDto.getLogin());
+ dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
+ });
}
private void removeGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToRemove, Map<String, GroupDto> groupsByName) {
- if (!groupsToRemove.isEmpty()) {
- for (String groupToRemove : groupsToRemove) {
- GroupDto groupDto = groupsByName.get(groupToRemove);
- if (groupDto != null) {
- LOGGER.debug("Removing group '{}' from user '{}'", groupDto.getName(), userDto.getLogin());
- dbClient.userGroupDao().delete(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
- }
- }
- }
+ groupsToRemove.stream().map(groupsByName::get).filter(groupDto -> groupDto != null).forEach(
+ groupDto -> {
+ LOGGER.debug("Removing group '{}' from user '{}'", groupDto.getName(), userDto.getLogin());
+ dbClient.userGroupDao().delete(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
+ });
}
private enum GroupDtoToName implements Function<GroupDto, String> {
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.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(JwtHttpHandler jwtHttpHandler) {
+ public ValidateJwtTokenFilter(Settings settings, JwtHttpHandler jwtHttpHandler, UserSession userSession) {
+ this.settings = settings;
this.jwtHttpHandler = jwtHttpHandler;
+ this.userSession = userSession;
}
@Override
return UrlPattern.builder()
.includes("/*")
.excludes(staticResourcePatterns())
+ .excludes(SKIPPED_URLS)
.build();
}
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) {
- response.setStatus(e.httpCode());
+ 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
import com.google.common.io.Resources;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
+import org.sonar.server.ws.ServletFilterHandler;
public class AuthenticationWs implements WebService {
@Override
public void define(Context context) {
NewController controller = context.createController("api/authentication");
- controller.setDescription("Check authentication credentials.");
+ controller.setDescription("Handle authentication.");
+ defineLoginAction(controller);
defineValidateAction(controller);
controller.done();
private void defineValidateAction(NewController controller) {
NewAction action = controller.createAction("validate")
- .setDescription("Check credentials")
+ .setDescription("Check credentials.")
.setSince("3.3")
.setHandler(RailsHandler.INSTANCE)
.setResponseExample(Resources.getResource(this.getClass(), "example-validate.json"));
RailsHandler.addFormatParam(action);
}
+ private static void defineLoginAction(NewController controller) {
+ NewAction action = controller.createAction("login")
+ .setDescription("Authenticate a user.")
+ .setSince("6.0")
+ .setPost(true)
+ .setHandler(ServletFilterHandler.INSTANCE);
+ action.createParam("login")
+ .setDescription("Login of the user")
+ .setRequired(true);
+ action.createParam("password")
+ .setDescription("Password of the user")
+ .setRequired(true);
+ }
+
}
*/
package org.sonar.server.user;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultimap;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.security.DefaultGroups;
+import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-
public abstract class AbstractUserSession<T extends AbstractUserSession> implements UserSession {
protected static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges";
private static final ForbiddenException INSUFFICIENT_PRIVILEGES_EXCEPTION = new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
+ protected UserDto userDto;
protected Integer userId;
protected String login;
+ protected String name;
+
protected Set<String> userGroups = Sets.newHashSet(DefaultGroups.ANYONE);
protected List<String> globalPermissions = Collections.emptyList();
protected HashMultimap<String, String> projectKeyByPermission = HashMultimap.create();
protected Map<String, String> projectUuidByComponentUuid = newHashMap();
protected List<String> projectPermissionsCheckedByKey = newArrayList();
protected List<String> projectPermissionsCheckedByUuid = newArrayList();
- protected String name;
+
protected Locale locale = Locale.ENGLISH;
private final Class<T> clazz;
this.clazz = clazz;
}
+
@Override
@CheckForNull
public String getLogin() {
return login;
}
- protected T setLogin(@Nullable String s) {
+ public T setLogin(@Nullable String s) {
this.login = Strings.emptyToNull(s);
return clazz.cast(this);
}
return name;
}
- protected T setName(@Nullable String s) {
+ public T setName(@Nullable String s) {
this.name = Strings.emptyToNull(s);
return clazz.cast(this);
}
return userId;
}
- protected T setUserId(@Nullable Integer userId) {
+ public T setUserId(@Nullable Integer userId) {
this.userId = userId;
return clazz.cast(this);
}
return userGroups;
}
- protected T setUserGroups(@Nullable String... userGroups) {
+ public T setUserGroups(@Nullable String... userGroups) {
if (userGroups != null) {
this.userGroups.addAll(Arrays.asList(userGroups));
}
+++ /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.user;
-
-import java.util.List;
-
-import javax.annotation.Nullable;
-
-import org.sonar.core.platform.ComponentContainer;
-import org.sonar.db.component.ResourceDao;
-import org.sonar.db.user.AuthorizationDao;
-import org.sonar.server.platform.Platform;
-import org.sonar.server.ui.JRubyI18n;
-
-import com.google.common.annotations.VisibleForTesting;
-
-public class RubyUserSession {
-
- private static RubyUserSession instance;
-
- private static RubyUserSession getInstance() {
- if (instance == null) {
- instance = new RubyUserSession(Platform.getInstance());
- }
- return instance;
- }
-
- private final Platform platform;
-
- /**
- * Invoked by Ruby code - see application_controller.rb
- */
- public static void setSession(@Nullable Integer userId, @Nullable String login, @Nullable String name, @Nullable List<String> userGroups, @Nullable String localeRubyKey) {
- getInstance().setSessionImpl(userId, login, name, userGroups, localeRubyKey);
- }
-
- @VisibleForTesting
- RubyUserSession(Platform platform) {
- // Utility class
- this.platform = platform;
- }
-
- public void setSessionImpl(@Nullable Integer userId, @Nullable String login, @Nullable String name, @Nullable List<String> userGroups, @Nullable String localeRubyKey) {
- ComponentContainer container = platform.getContainer();
- ThreadLocalUserSession threadLocalUserSession = container.getComponentByType(ThreadLocalUserSession.class);
-
- UserSession session = new ServerUserSession(container.getComponentByType(AuthorizationDao.class),
- container.getComponentByType(ResourceDao.class))
- .setLogin(login)
- .setName(name)
- .setUserId(userId)
- .setUserGroups(userGroups != null ? userGroups.toArray(new String[userGroups.size()]) : null)
- .setLocale(JRubyI18n.toLocale(localeRubyKey));
- threadLocalUserSession.set(session);
- }
-
-}
*/
package org.sonar.server.user;
+import static com.google.common.collect.Maps.newHashMap;
+import static java.util.Objects.requireNonNull;
+
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import org.sonar.api.security.DefaultGroups;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.db.component.ResourceDao;
import org.sonar.db.component.ResourceDto;
import org.sonar.db.user.AuthorizationDao;
-
-import static com.google.common.collect.Maps.newHashMap;
-import static com.google.common.collect.Sets.newHashSet;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
/**
* Part of the current HTTP session
*/
public class ServerUserSession extends AbstractUserSession<ServerUserSession> {
-
private Map<String, String> projectKeyByComponentKey = newHashMap();
+ private final DbClient dbClient;
private final AuthorizationDao authorizationDao;
private final ResourceDao resourceDao;
- ServerUserSession(AuthorizationDao authorizationDao, ResourceDao resourceDao) {
+ private ServerUserSession(DbClient dbClient, @Nullable UserDto userDto) {
super(ServerUserSession.class);
+ this.dbClient = dbClient;
+ this.authorizationDao = dbClient.authorizationDao();
+ this.resourceDao = dbClient.resourceDao();
this.globalPermissions = null;
- this.authorizationDao = authorizationDao;
- this.resourceDao = resourceDao;
- // Do not forget that when forceAuthentication is set to true, the Anyone group should not be set (but this will be check when
- // authentication will be done in Java)
- this.userGroups = newHashSet(DefaultGroups.ANYONE);
+ if(userDto != null){
+ this.setLogin(userDto.getLogin());
+ this.setName(userDto.getName());
+ this.setUserId(userDto.getId().intValue());
+ this.userGroups.addAll(getUserGroups(userDto.getLogin()));
+ }
+ }
+
+ public static ServerUserSession createForUser(DbClient dbClient, UserDto userDto){
+ requireNonNull(userDto, "UserDto must not be null");
+ return new ServerUserSession(dbClient, userDto);
+ }
+
+ public static ServerUserSession createForAnonymous(DbClient dbClient){
+ return new ServerUserSession(dbClient, null);
+ }
+
+ private Set<String> getUserGroups(String userLogin) {
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ return new HashSet<>(dbClient.groupDao().selectByUserLogin(dbSession, userLogin).stream().map(GroupDto::getName).collect(Collectors.toSet()));
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
}
@Override
*/
package org.sonar.server.user;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import static com.google.common.base.Preconditions.checkNotNull;
-
public class UpdateUser {
private String login;
private String password;
private ExternalIdentity externalIdentity;
- boolean nameChanged;
- boolean emailChanged;
- boolean scmAccountsChanged;
- boolean passwordChanged;
- boolean externalIdentityChanged;
+ private boolean nameChanged;
+ private boolean emailChanged;
+ private boolean scmAccountsChanged;
+ private boolean passwordChanged;
+ private boolean externalIdentityChanged;
private UpdateUser(String login) {
// No direct call to this constructor
return externalIdentity;
}
+ /**
+ * This method should only be used when updating a none local user
+ */
public UpdateUser setExternalIdentity(@Nullable ExternalIdentity externalIdentity) {
this.externalIdentity = externalIdentity;
externalIdentityChanged = true;
*/
package org.sonar.server.user;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.db.user.UserDto.encryptPassword;
+
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.util.Validation;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static com.google.common.collect.Lists.newArrayList;
-
@ServerSide
public class UserUpdater {
UpdateUser updateUser = UpdateUser.create(login)
.setName(newUser.name())
.setEmail(newUser.email())
- .setScmAccounts(newUser.scmAccounts())
- .setExternalIdentity(newUser.externalIdentity());
+ .setScmAccounts(newUser.scmAccounts());
if (newUser.password() != null) {
updateUser.setPassword(newUser.password());
}
+ if (newUser.externalIdentity() != null) {
+ updateUser.setExternalIdentity(newUser.externalIdentity());
+ }
// Hack to allow to change the password of the user
existingUser.setLocal(true);
updateUserDto(dbSession, updateUser, existingUser);
List<Message> messages = newArrayList();
String login = newUser.login();
- validateLoginFormat(login, messages);
- userDto.setLogin(login);
+ if (validateLoginFormat(login, messages)) {
+ userDto.setLogin(login);
+ }
String name = newUser.name();
- validateNameFormat(name, messages);
- userDto.setName(name);
+ if (validateNameFormat(name, messages)) {
+ userDto.setName(name);
+ }
String email = newUser.email();
- if (email != null) {
- validateEmailFormat(email, messages);
+ if (email != null && validateEmailFormat(email, messages)) {
userDto.setEmail(email);
}
String password = newUser.password();
- if (password != null) {
- validatePasswords(password, messages);
+ if (password != null && validatePasswords(password, messages)) {
setEncryptedPassWord(password, userDto);
}
List<Message> messages = newArrayList();
String name = updateUser.name();
- if (updateUser.isNameChanged()) {
- validateNameFormat(name, messages);
+ if (updateUser.isNameChanged() && validateNameFormat(name, messages)) {
userDto.setName(name);
}
String email = updateUser.email();
- if (updateUser.isEmailChanged()) {
- validateEmailFormat(email, messages);
+ if (updateUser.isEmailChanged() && validateEmailFormat(email, messages)) {
userDto.setEmail(email);
}
- if (isNewExternalIdentityNotEqualsToSonaQube(updateUser)) {
+ if (updateUser.isExternalIdentityChanged()) {
setExternalIdentity(userDto, updateUser.externalIdentity());
userDto.setSalt(null);
userDto.setCryptedPassword(null);
} else {
String password = updateUser.password();
- if (updateUser.isPasswordChanged()) {
- validatePasswords(password, messages);
- checkPasswordChangeAllowed(userDto, messages);
+ if (updateUser.isPasswordChanged() && validatePasswords(password, messages) && checkPasswordChangeAllowed(userDto, messages)) {
setEncryptedPassWord(password, userDto);
}
}
if (updateUser.isScmAccountsChanged()) {
List<String> scmAccounts = sanitizeScmAccounts(updateUser.scmAccounts());
if (scmAccounts != null && !scmAccounts.isEmpty()) {
- validateScmAccounts(dbSession, scmAccounts, userDto.getLogin(), email != null ? email : userDto.getEmail(), userDto, messages);
- userDto.setScmAccounts(scmAccounts);
+ String newOrOldEmail = email != null ? email : userDto.getEmail();
+ if (validateScmAccounts(dbSession, scmAccounts, userDto.getLogin(), newOrOldEmail, userDto, messages)) {
+ userDto.setScmAccounts(scmAccounts);
+ }
} else {
userDto.setScmAccounts((String) null);
}
}
- if (updateUser.isExternalIdentityChanged()) {
- setExternalIdentity(userDto, updateUser.externalIdentity());
- }
-
if (!messages.isEmpty()) {
throw new BadRequestException(messages);
}
}
}
- private static void checkNotEmptyParam(@Nullable String value, String param, List<Message> messages) {
+ private static boolean checkNotEmptyParam(@Nullable String value, String param, List<Message> messages) {
if (isNullOrEmpty(value)) {
messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, param));
+ return false;
}
+ return true;
}
- private static String validateLoginFormat(@Nullable String login, List<Message> messages) {
- checkNotEmptyParam(login, LOGIN_PARAM, messages);
+ private static boolean validateLoginFormat(@Nullable String login, List<Message> messages) {
+ boolean isValid = checkNotEmptyParam(login, LOGIN_PARAM, messages);
if (!isNullOrEmpty(login)) {
if (login.length() < LOGIN_MIN_LENGTH) {
messages.add(Message.of(Validation.IS_TOO_SHORT_MESSAGE, LOGIN_PARAM, LOGIN_MIN_LENGTH));
+ return false;
} else if (login.length() > LOGIN_MAX_LENGTH) {
messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, LOGIN_PARAM, LOGIN_MAX_LENGTH));
+ return false;
} else if (!login.matches("\\A\\w[\\w\\.\\-_@]+\\z")) {
messages.add(Message.of("user.bad_login"));
+ return false;
}
}
- return login;
+ return isValid;
}
- private static void validateNameFormat(@Nullable String name, List<Message> messages) {
- checkNotEmptyParam(name, NAME_PARAM, messages);
+ private static boolean validateNameFormat(@Nullable String name, List<Message> messages) {
+ boolean isValid = checkNotEmptyParam(name, NAME_PARAM, messages);
if (name != null && name.length() > NAME_MAX_LENGTH) {
messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, NAME_PARAM, 200));
+ return false;
}
+ return isValid;
}
- private static void validateEmailFormat(@Nullable String email, List<Message> messages) {
+ private static boolean validateEmailFormat(@Nullable String email, List<Message> messages) {
if (email != null && email.length() > EMAIL_MAX_LENGTH) {
messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, EMAIL_PARAM, 100));
+ return false;
}
+ return true;
}
- private static void checkPasswordChangeAllowed(UserDto userDto, List<Message> messages) {
+ private static boolean checkPasswordChangeAllowed(UserDto userDto, List<Message> messages) {
if (!userDto.isLocal()) {
messages.add(Message.of("user.password_cant_be_changed_on_external_auth"));
+ return false;
}
+ return true;
}
- private static boolean isNewExternalIdentityNotEqualsToSonaQube(UpdateUser updateUser) {
- ExternalIdentity externalIdentity = updateUser.externalIdentity();
- if (updateUser.isExternalIdentityChanged() && externalIdentity != null) {
- return !externalIdentity.getProvider().equals(SQ_AUTHORITY);
- }
- return false;
- }
-
- private static void validatePasswords(@Nullable String password, List<Message> messages) {
+ private static boolean validatePasswords(@Nullable String password, List<Message> messages) {
if (password == null || password.length() == 0) {
messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, PASSWORD_PARAM));
+ return false;
}
+ return true;
}
- private void validateScmAccounts(DbSession dbSession, List<String> scmAccounts, @Nullable String login, @Nullable String email, @Nullable UserDto existingUser,
+ private boolean validateScmAccounts(DbSession dbSession, List<String> scmAccounts, @Nullable String login, @Nullable String email, @Nullable UserDto existingUser,
List<Message> messages) {
+ boolean isValid = true;
for (String scmAccount : scmAccounts) {
if (scmAccount.equals(login) || scmAccount.equals(email)) {
messages.add(Message.of("user.login_or_email_used_as_scm_account"));
+ isValid = false;
} else {
List<UserDto> matchingUsers = dbClient.userDao().selectByScmAccountOrLoginOrEmail(dbSession, scmAccount);
List<String> matchingUsersWithoutExistingUser = newArrayList();
}
if (!matchingUsersWithoutExistingUser.isEmpty()) {
messages.add(Message.of("user.scm_account_already_used", scmAccount, Joiner.on(", ").join(matchingUsersWithoutExistingUser)));
+ isValid = false;
}
}
}
+ return isValid;
}
@CheckForNull
userDto.setCryptedPassword(encryptPassword(password, saltHex));
}
- private static String encryptPassword(String password, String salt) {
- return DigestUtils.sha1Hex("--" + salt + "--" + password + "--");
- }
-
private void notifyNewUser(String login, String name, String email) {
newUserNotifier.onNewUser(NewUserHandler.Context.builder()
.setLogin(login)
--- /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.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+
+/**
+ * Used to declare web services that are implemented by a servlet filter.
+ */
+public class ServletFilterHandler implements RequestHandler {
+
+ public static final RequestHandler INSTANCE = new ServletFilterHandler();
+
+ private ServletFilterHandler() {
+ // Nothing
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+ throw new UnsupportedOperationException("This web service is implemented as a servlet filter");
+ }
+
+}
--- /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);
+ }
+}
package org.sonar.server.authentication;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.sonar.api.platform.Server;
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.db.user.UserDto;
public class BaseContextFactoryTest {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class);
+ JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
- BaseContextFactory underTest = new BaseContextFactory(userIdentityAuthenticator, server);
+ BaseContextFactory underTest = new BaseContextFactory(userIdentityAuthenticator, server, jwtHttpHandler);
@Before
public void setUp() throws Exception {
when(request.getSession()).thenReturn(session);
context.authenticate(USER_IDENTITY);
- verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider, request, response);
+ verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
+ verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(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;
+
+import static org.assertj.core.api.Assertions.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.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.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.server.exceptions.UnauthorizedException;
+
+public class CredentialsAuthenticatorTest {
+
+ static final String LOGIN = "LOGIN";
+ static final String PASSWORD = "PASSWORD";
+ static final String SALT = "0242b0b4c0a93ddfe09dd886de50bc25ba000b51";
+ static final String CRYPTED_PASSWORD = "540e4fc4be4e047db995bc76d18374a5b5db08cc";
+
+ @Rule
+ public ExpectedException expectedException = none();
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ RealmAuthenticator externalAuthenticator = mock(RealmAuthenticator.class);
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ CredentialsAuthenticator underTest = new CredentialsAuthenticator(dbClient, externalAuthenticator);
+
+ @Test
+ public void authenticate_local_user() throws Exception {
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setCryptedPassword(CRYPTED_PASSWORD)
+ .setSalt(SALT)
+ .setLocal(true));
+
+ UserDto userDto = executeAuthenticate();
+ assertThat(userDto.getLogin()).isEqualTo(LOGIN);
+ }
+
+ @Test
+ public void fail_to_authenticate_local_user_when_password_is_wrong() throws Exception {
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setCryptedPassword("Wrong password")
+ .setSalt("Wrong salt")
+ .setLocal(true));
+
+ expectedException.expect(UnauthorizedException.class);
+ executeAuthenticate();
+ }
+
+ @Test
+ public void authenticate_external_user() throws Exception {
+ when(externalAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(Optional.of(newUserDto()));
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setLocal(false));
+
+ executeAuthenticate();
+
+ verify(externalAuthenticator).authenticate(LOGIN, PASSWORD, request);
+ }
+
+ @Test
+ public void fail_to_authenticate_authenticate_external_user_when_no_external_authentication() throws Exception {
+ when(externalAuthenticator.authenticate(LOGIN, PASSWORD, request)).thenReturn(Optional.empty());
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setLocal(false));
+
+ expectedException.expect(UnauthorizedException.class);
+ executeAuthenticate();
+ }
+
+ @Test
+ public void fail_to_authenticate_local_user_that_have_no_password() throws Exception {
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setCryptedPassword(null)
+ .setSalt(SALT)
+ .setLocal(true));
+
+ expectedException.expect(UnauthorizedException.class);
+ executeAuthenticate();
+ }
+
+ @Test
+ public void fail_to_authenticate_local_user_that_have_no_salt() throws Exception {
+ insertUser(newUserDto()
+ .setLogin(LOGIN)
+ .setCryptedPassword(CRYPTED_PASSWORD)
+ .setSalt(null)
+ .setLocal(true));
+
+ expectedException.expect(UnauthorizedException.class);
+ executeAuthenticate();
+ }
+
+ private UserDto executeAuthenticate(){
+ return underTest.authenticate(LOGIN, PASSWORD, request);
+ }
+
+ private UserDto insertUser(UserDto userDto){
+ dbClient.userDao().insert(dbSession, userDto);
+ dbSession.commit();
+ return 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 org.assertj.core.api.Assertions.assertThat;
-import static org.jboss.netty.handler.codec.http.HttpMethod.GET;
-import static org.jboss.netty.handler.codec.http.HttpMethod.POST;
-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.ServletOutputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.server.tester.UserSessionRule;
-
-public class GenerateJwtTokenFilterTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @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);
-
- GenerateJwtTokenFilter underTest = new GenerateJwtTokenFilter(jwtHttpHandler, userSession);
-
- @Before
- public void setUp() throws Exception {
- when(response.getOutputStream()).thenReturn(mock(ServletOutputStream.class));
- }
-
- @Test
- public void do_get_pattern() throws Exception {
- assertThat(underTest.doGetPattern().matches("/sessions/login")).isTrue();
- assertThat(underTest.doGetPattern().matches("/")).isFalse();
- }
-
- @Test
- public void create_session_when_post_request_and_user_is_authenticated() throws Exception {
- executePostRequest();
- userSession.login("john");
-
- underTest.doFilter(request, response, chain);
-
- verify(jwtHttpHandler).generateToken("john", response);
- }
-
- @Test
- public void does_nothing_on_get_request() throws Exception {
- executeGetRequest();
- userSession.login("john");
-
- underTest.doFilter(request, response, chain);
-
- verifyZeroInteractions(jwtHttpHandler);
- }
-
- @Test
- public void does_nothing_when_user_is_not_authenticated() throws Exception {
- executePostRequest();
- userSession.anonymous();
-
- underTest.doFilter(request, response, chain);
-
- verifyZeroInteractions(jwtHttpHandler);
- }
-
- private void executePostRequest() {
- when(request.getMethod()).thenReturn(POST.getName());
- }
-
- private void executeGetRequest() {
- when(request.getMethod()).thenReturn(GET.getName());
- }
-
-}
*/
package org.sonar.server.authentication;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
-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 InitFilterTest {
static String OAUTH2_PROVIDER_KEY = "github";
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.api.utils.System2.INSTANCE;
+import static org.sonar.db.user.UserTesting.newUserDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.impl.DefaultClaims;
import 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.tester.UserSessionRule;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.ServerUserSession;
+import org.sonar.server.user.ThreadLocalUserSession;
public class JwtHttpHandlerTest {
static final String JWT_TOKEN = "TOKEN";
- static final String USER_LOGIN = "john";
static final String CSRF_STATE = "CSRF_STATE";
+ static final String USER_LOGIN = "john";
static final long NOW = 10_000_000_000L;
static final long FOUR_MINUTES_AGO = NOW - 4 * 60 * 1000L;
@Rule
public ExpectedException thrown = ExpectedException.none();
- @Rule
- public UserSessionRule userSession = UserSessionRule.standalone();
-
@Rule
public DbTester dbTester = DbTester.create(INSTANCE);
+ ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
+
DbClient dbClient = dbTester.getDbClient();
DbSession dbSession = dbTester.getSession();
JwtSerializer jwtSerializer = mock(JwtSerializer.class);
JwtCsrfVerifier jwtCsrfVerifier = mock(JwtCsrfVerifier.class);
- JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
+ UserDto userDto = newUserDto().setLogin(USER_LOGIN);
+
+ JwtHttpHandler underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
@Before
public void setUp() throws Exception {
+ threadLocalUserSession.remove();
when(system2.now()).thenReturn(NOW);
when(server.isSecured()).thenReturn(true);
when(request.getSession()).thenReturn(httpSession);
when(jwtSerializer.encode(any(JwtSerializer.JwtSession.class))).thenReturn(JWT_TOKEN);
when(jwtCsrfVerifier.generateState(eq(response), anyInt())).thenReturn(CSRF_STATE);
+ dbClient.userDao().insert(dbSession, userDto);
+ dbSession.commit();
}
@Test
- public void create_session() throws Exception {
- underTest.generateToken(USER_LOGIN, response);
+ public void create_token() throws Exception {
+ underTest.generateToken(userDto, response);
Optional<Cookie> jwtCookie = findCookie("JWT-SESSION");
assertThat(jwtCookie).isPresent();
verify(jwtSerializer).encode(jwtArgumentCaptor.capture());
verifyToken(jwtArgumentCaptor.getValue(), 3 * 24 * 60 * 60, NOW);
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
- public void generate_csrf_state() throws Exception {
- underTest.generateToken(USER_LOGIN, response);
+ public void generate_csrf_state_when_creating_token() throws Exception {
+ underTest.generateToken(userDto, response);
verify(jwtCsrfVerifier).generateState(response, 3 * 24 * 60 * 60);
}
@Test
- public void validate_session() throws Exception {
- addJwtCookie();
- UserDto user = addUser();
-
- Claims claims = createToken(NOW);
- when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
-
- underTest.validateToken(request, response);
-
- verify(httpSession).setAttribute("user_id", user.getId());
- verify(jwtSerializer, never()).encode(any(JwtSerializer.JwtSession.class));
- }
-
- @Test
- public void use_session_timeout_from_settings() throws Exception {
+ public void generate_token_is_using_session_timeout_from_settings() throws Exception {
int sessionTimeoutInHours = 10;
settings.setProperty("sonar.auth.sessionTimeoutInHours", sessionTimeoutInHours);
- underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
- underTest.generateToken(USER_LOGIN, response);
+ underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
+ underTest.generateToken(userDto, response);
verify(jwtSerializer).encode(jwtArgumentCaptor.capture());
verifyToken(jwtArgumentCaptor.getValue(), sessionTimeoutInHours * 60 * 60, NOW);
int firstSessionTimeoutInHours = 10;
settings.setProperty("sonar.auth.sessionTimeoutInHours", firstSessionTimeoutInHours);
- underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier);
- underTest.generateToken(USER_LOGIN, response);
+ underTest = new JwtHttpHandler(system2, dbClient, server, settings, jwtSerializer, jwtCsrfVerifier, threadLocalUserSession);
+ underTest.generateToken(userDto, response);
// The property is updated, but it won't be taking into account
settings.setProperty("sonar.auth.sessionTimeoutInHours", 15);
- underTest.generateToken(USER_LOGIN, response);
+ underTest.generateToken(userDto, response);
verify(jwtSerializer, times(2)).encode(jwtArgumentCaptor.capture());
verifyToken(jwtArgumentCaptor.getAllValues().get(0), firstSessionTimeoutInHours * 60 * 60, NOW);
verifyToken(jwtArgumentCaptor.getAllValues().get(1), firstSessionTimeoutInHours * 60 * 60, NOW);
}
@Test
- public void refresh_session_when_refresh_time_is_reached() throws Exception {
+ public void validate_token() throws Exception {
+ addJwtCookie();
+
+ Claims claims = createToken(USER_LOGIN, NOW);
+ when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
+
+ underTest.validateToken(request, response);
+
+ verify(jwtSerializer, never()).encode(any(JwtSerializer.JwtSession.class));
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
+ }
+
+ @Test
+ public void validate_token_refresh_session_when_refresh_time_is_reached() throws Exception {
addJwtCookie();
- UserDto user = addUser();
// Token was created 10 days ago and refreshed 6 minutes ago
- Claims claims = createToken(TEN_DAYS_AGO);
+ Claims claims = createToken(USER_LOGIN, TEN_DAYS_AGO);
claims.put("lastRefreshTime", SIX_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
- verify(httpSession).setAttribute("user_id", user.getId());
verify(jwtSerializer).refresh(any(Claims.class), eq(3 * 24 * 60 * 60));
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
- public void does_not_refresh_session_when_refresh_time_is_not_reached() throws Exception {
+ public void validate_token_does_not_refresh_session_when_refresh_time_is_not_reached() throws Exception {
addJwtCookie();
- UserDto user = addUser();
// Token was created 10 days ago and refreshed 4 minutes ago
- Claims claims = createToken(TEN_DAYS_AGO);
+ Claims claims = createToken(USER_LOGIN, TEN_DAYS_AGO);
claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
- verify(httpSession).setAttribute("user_id", user.getId());
verify(jwtSerializer, never()).refresh(any(Claims.class), anyInt());
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isTrue();
}
@Test
- public void remove_session_when_disconnected_timeout_is_reached() throws Exception {
+ public void validate_token_removes_session_when_disconnected_timeout_is_reached() throws Exception {
addJwtCookie();
- addUser();
// Token was created 4 months ago, refreshed 4 minutes ago, and it expired in 5 minutes
- Claims claims = createToken(NOW - (4L * 30 * 24 * 60 * 60 * 1000));
+ Claims claims = createToken(USER_LOGIN, NOW - (4L * 30 * 24 * 60 * 60 * 1000));
claims.setExpiration(new Date(NOW + 5 * 60 * 1000));
claims.put("lastRefreshTime", FOUR_MINUTES_AGO);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
underTest.validateToken(request, response);
- verify(httpSession).removeAttribute("user_id");
verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
- public void remove_session_when_user_is_disabled() throws Exception {
+ public void validate_token_fails_with_unauthorized_when_user_is_disabled() throws Exception {
addJwtCookie();
- addUser(false);
+ UserDto user = addUser(false);
- Claims claims = createToken(NOW);
+ Claims claims = createToken(user.getLogin(), NOW);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
+ thrown.expect(UnauthorizedException.class);
underTest.validateToken(request, response);
-
- verify(httpSession).removeAttribute("user_id");
- verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
}
@Test
- public void remove_session_when_token_is_no_more_valid() throws Exception {
+ public void validate_token_removes_session_when_token_is_no_more_valid() throws Exception {
addJwtCookie();
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.empty());
underTest.validateToken(request, response);
- verify(httpSession).removeAttribute("user_id");
verifyCookie(findCookie("JWT-SESSION").get(), null, 0);
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
- public void does_nothing_when_no_jwt_cookie() throws Exception {
+ public void validate_token_does_nothing_when_no_jwt_cookie() throws Exception {
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
- public void does_nothing_when_empty_value_in_jwt_cookie() throws Exception {
+ public void validate_token_does_nothing_when_empty_value_in_jwt_cookie() throws Exception {
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("JWT-SESSION", "")});
underTest.validateToken(request, response);
verifyZeroInteractions(httpSession, jwtSerializer);
+ assertThat(threadLocalUserSession.get().isLoggedIn()).isFalse();
}
@Test
- public void verify_csrf_state() throws Exception {
+ public void validate_token_verify_csrf_state() throws Exception {
addJwtCookie();
- addUser();
- Claims claims = createToken(NOW);
+ Claims claims = createToken(USER_LOGIN, NOW);
claims.put("xsrfToken", CSRF_STATE);
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
}
@Test
- public void refresh_state_when_refreshing_token() throws Exception {
+ public void validate_token_refresh_state_when_refreshing_token() throws Exception {
addJwtCookie();
- addUser();
// Token was created 10 days ago and refreshed 6 minutes ago
- Claims claims = createToken(TEN_DAYS_AGO);
+ Claims claims = createToken(USER_LOGIN, TEN_DAYS_AGO);
claims.put("xsrfToken", "CSRF_STATE");
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.of(claims));
}
@Test
- public void remove_state_when_removing_token() throws Exception {
+ public void validate_token_remove_state_when_removing_token() throws Exception {
addJwtCookie();
// Token is invalid => it will be removed
when(jwtSerializer.decode(JWT_TOKEN)).thenReturn(Optional.empty());
verify(jwtCsrfVerifier).removeState(response);
}
+ @Test
+ public void remove_token() throws Exception {
+ underTest.removeToken(response);
+
+ 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) {
assertThat(token.getExpirationTimeInSeconds()).isEqualTo(expectedExpirationTime);
assertThat(token.getUserLogin()).isEqualTo(USER_LOGIN);
assertThat(cookie.getValue()).isEqualTo(value);
}
- private UserDto addUser() {
- return addUser(true);
- }
-
private UserDto addUser(boolean active) {
- UserDto user = UserTesting.newUserDto()
- .setLogin(USER_LOGIN)
+ UserDto user = newUserDto()
.setActive(active);
dbClient.userDao().insert(dbSession, user);
dbSession.commit();
return cookie;
}
- private Claims createToken(long createdAt) {
+ private Claims createToken(String userLogin, long createdAt) {
// Expired in 5 minutes by default
- return createToken(createdAt, NOW + 5 * 60 * 1000);
+ return createToken(userLogin, createdAt, NOW + 5 * 60 * 1000);
}
- private Claims createToken(long createdAt, long expiredAt) {
+ private Claims createToken(String userLogin, long createdAt, long expiredAt) {
DefaultClaims claims = new DefaultClaims();
claims.setId("ID");
- claims.setSubject(USER_LOGIN);
+ claims.setSubject(userLogin);
claims.setIssuedAt(new Date(createdAt));
claims.setExpiration(new Date(expiredAt));
claims.put("lastRefreshTime", createdAt);
*/
package org.sonar.server.authentication;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
-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 OAuth2CallbackFilterTest {
static String OAUTH2_PROVIDER_KEY = "github";
assertError("Fail to callback authentication");
}
- private void assertCallbackCalled(){
+ private void assertCallbackCalled() {
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
assertThat(oAuth2IdentityProvider.isCallbackCalled()).isTrue();
}
package org.sonar.server.authentication;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.api.utils.MessageException;
+import org.sonar.db.user.UserDto;
public class OAuth2ContextFactoryTest {
UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
Server server = mock(Server.class);
OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
+ JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
HttpSession session = mock(HttpSession.class);
OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class);
- OAuth2ContextFactory underTest = new OAuth2ContextFactory(userIdentityAuthenticator, server, csrfVerifier);
+ OAuth2ContextFactory underTest = new OAuth2ContextFactory(userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler);
@Before
public void setUp() throws Exception {
public void create_context() throws Exception {
when(server.getPublicRootUrl()).thenReturn(SECURED_PUBLIC_ROOT_URL);
- OAuth2IdentityProvider.InitContext context = underTest.newContext(request, response, identityProvider);
+ OAuth2IdentityProvider.InitContext context = newInitContext();
assertThat(context.getRequest()).isEqualTo(request);
assertThat(context.getResponse()).isEqualTo(response);
@Test
public void generate_csrf_state() throws Exception {
- OAuth2IdentityProvider.InitContext context = underTest.newContext(request, response, identityProvider);
+ OAuth2IdentityProvider.InitContext context = newInitContext();
context.generateCsrfState();
@Test
public void redirect_to() throws Exception {
- OAuth2IdentityProvider.InitContext context = underTest.newContext(request, response, identityProvider);
+ OAuth2IdentityProvider.InitContext context = newInitContext();
context.redirectTo("/test");
public void fail_to_get_callback_url_on_not_secured_server() throws Exception {
when(server.getPublicRootUrl()).thenReturn(NOT_SECURED_PUBLIC_URL);
- OAuth2IdentityProvider.InitContext context = underTest.newContext(request, response, identityProvider);
+ OAuth2IdentityProvider.InitContext context = newInitContext();
thrown.expect(MessageException.class);
thrown.expectMessage("The server url should be configured in https, please update the property 'sonar.core.serverBaseURL'");
public void create_callback() throws Exception {
when(server.getPublicRootUrl()).thenReturn(SECURED_PUBLIC_ROOT_URL);
- OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+ OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
assertThat(callback.getRequest()).isEqualTo(request);
assertThat(callback.getResponse()).isEqualTo(response);
@Test
public void authenticate() throws Exception {
- OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+ OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
callback.authenticate(USER_IDENTITY);
- verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider, request, response);
+ verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
+ verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(response));
}
@Test
public void redirect_to_requested_page() throws Exception {
when(server.getContextPath()).thenReturn("");
- OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+ OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
callback.redirectToRequestedPage();
@Test
public void redirect_to_requested_page_with_context() throws Exception {
when(server.getContextPath()).thenReturn("/sonarqube");
- OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+ OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
callback.redirectToRequestedPage();
@Test
public void verify_csrf_state() throws Exception {
- OAuth2IdentityProvider.CallbackContext callback = underTest.newCallback(request, response, identityProvider);
+ OAuth2IdentityProvider.CallbackContext callback = newCallbackContext();
callback.verifyCsrfState();
verify(csrfVerifier).verifyState(request, response);
}
+
+ private OAuth2IdentityProvider.InitContext newInitContext() {
+ return underTest.newContext(request, response, identityProvider);
+ }
+
+ private OAuth2IdentityProvider.CallbackContext newCallbackContext() {
+ return underTest.newCallback(request, response, identityProvider);
+ }
+
}
--- /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.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.rules.ExpectedException.none;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+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.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.config.Settings;
+import org.sonar.api.security.Authenticator;
+import org.sonar.api.security.ExternalGroupsProvider;
+import org.sonar.api.security.ExternalUsersProvider;
+import org.sonar.api.security.SecurityRealm;
+import org.sonar.api.security.UserDetails;
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.user.SecurityRealmFactory;
+
+public class RealmAuthenticatorTest {
+
+ @Rule
+ public ExpectedException expectedException = none();
+
+ static final String LOGIN = "LOGIN";
+ static final String PASSWORD = "PASSWORD";
+
+ static final UserDto USER = newUserDto();
+
+ ArgumentCaptor<UserIdentity> userIdentityArgumentCaptor = ArgumentCaptor.forClass(UserIdentity.class);
+ ArgumentCaptor<IdentityProvider> identityProviderArgumentCaptor = ArgumentCaptor.forClass(IdentityProvider.class);
+
+ Settings settings = new Settings();
+
+ SecurityRealmFactory securityRealmFactory = mock(SecurityRealmFactory.class);
+ SecurityRealm realm = mock(SecurityRealm.class);
+ Authenticator authenticator = mock(Authenticator.class);
+ ExternalUsersProvider externalUsersProvider = mock(ExternalUsersProvider.class);
+ ExternalGroupsProvider externalGroupsProvider = mock(ExternalGroupsProvider.class);
+
+ UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ RealmAuthenticator underTest = new RealmAuthenticator(settings, securityRealmFactory, userIdentityAuthenticator);
+
+ @Test
+ public void authenticate() throws Exception {
+ executeStartWithoutGroupSync();
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+ UserDetails userDetails = new UserDetails();
+ userDetails.setName("name");
+ userDetails.setEmail("email");
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+
+ underTest.authenticate(LOGIN, PASSWORD, request);
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
+ assertThat(userIdentity.getLogin()).isEqualTo(LOGIN);
+ assertThat(userIdentity.getProviderLogin()).isEqualTo(LOGIN);
+ assertThat(userIdentity.getName()).isEqualTo("name");
+ assertThat(userIdentity.getEmail()).isEqualTo("email");
+ assertThat(userIdentity.shouldSyncGroups()).isFalse();
+ }
+
+ @Test
+ public void authenticate_with_sonarqube_identity_provider() throws Exception {
+ executeStartWithoutGroupSync();
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+ UserDetails userDetails = new UserDetails();
+ userDetails.setName("name");
+ userDetails.setEmail("email");
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+
+ underTest.authenticate(LOGIN, PASSWORD, request);
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+
+ assertThat(identityProviderArgumentCaptor.getValue().getKey()).isEqualTo("sonarqube");
+ assertThat(identityProviderArgumentCaptor.getValue().getName()).isEqualTo("sonarqube");
+ assertThat(identityProviderArgumentCaptor.getValue().getDisplay()).isNull();
+ assertThat(identityProviderArgumentCaptor.getValue().isEnabled()).isTrue();
+ }
+
+ @Test
+ public void login_is_used_when_no_name_provided() throws Exception {
+ executeStartWithoutGroupSync();
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+ UserDetails userDetails = new UserDetails();
+ userDetails.setEmail("email");
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+
+ underTest.authenticate(LOGIN, PASSWORD, request);
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ assertThat(identityProviderArgumentCaptor.getValue().getName()).isEqualTo("sonarqube");
+ }
+
+ @Test
+ public void authenticate_with_group_sync() throws Exception {
+ when(externalGroupsProvider.doGetGroups(any(ExternalGroupsProvider.Context.class))).thenReturn(asList("group1", "group2"));
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+ executeStartWithGroupSync();
+ executeAuthenticate();
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+
+ UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
+ assertThat(userIdentity.shouldSyncGroups()).isTrue();
+ assertThat(userIdentity.getGroups()).containsOnly("group1", "group2");
+ }
+
+ @Test
+ public void use_login_if_user_details_contains_no_name() throws Exception {
+ executeStartWithoutGroupSync();
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+ UserDetails userDetails = new UserDetails();
+ userDetails.setName(null);
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+
+ underTest.authenticate(LOGIN, PASSWORD, request);
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ assertThat(userIdentityArgumentCaptor.getValue().getName()).isEqualTo(LOGIN);
+ }
+
+ @Test
+ public void allow_to_sign_up_property() throws Exception {
+ settings.setProperty("sonar.authenticator.createUsers", true);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+ executeStartWithoutGroupSync();
+ executeAuthenticate();
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ assertThat(identityProviderArgumentCaptor.getValue().allowsUsersToSignUp()).isTrue();
+ }
+
+ @Test
+ public void does_not_allow_to_sign_up_property() throws Exception {
+ settings.setProperty("sonar.authenticator.createUsers", false);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+ executeStartWithoutGroupSync();
+ executeAuthenticate();
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ assertThat(identityProviderArgumentCaptor.getValue().allowsUsersToSignUp()).isFalse();
+ }
+
+ @Test
+ public void use_downcase_login() throws Exception {
+ settings.setProperty("sonar.authenticator.downcase", true);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+ executeStartWithoutGroupSync();
+ executeAuthenticate("LOGIN");
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
+ assertThat(userIdentity.getLogin()).isEqualTo("login");
+ assertThat(userIdentity.getProviderLogin()).isEqualTo("login");
+ }
+
+ @Test
+ public void does_not_user_downcase_login() throws Exception {
+ settings.setProperty("sonar.authenticator.downcase", false);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class))).thenReturn(USER);
+ executeStartWithoutGroupSync();
+ executeAuthenticate("LoGiN");
+
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
+ assertThat(userIdentity.getLogin()).isEqualTo("LoGiN");
+ assertThat(userIdentity.getProviderLogin()).isEqualTo("LoGiN");
+ }
+
+ @Test
+ public void fail_to_authenticate_when_user_details_are_null() throws Exception {
+ executeStartWithoutGroupSync();
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(LOGIN, PASSWORD, request);
+ }
+
+ @Test
+ public void fail_to_authenticate_when_external_authentication_fails() throws Exception {
+ executeStartWithoutGroupSync();
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
+
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(false);
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(LOGIN, PASSWORD, request);
+ }
+
+ @Test
+ public void fail_to_authenticate_when_any_exception_is_thrown() throws Exception {
+ executeStartWithoutGroupSync();
+ doThrow(IllegalArgumentException.class).when(authenticator).doAuthenticate(any(Authenticator.Context.class));
+
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
+
+ expectedException.expect(UnauthorizedException.class);
+ underTest.authenticate(LOGIN, PASSWORD, request);
+ }
+
+ @Test
+ public void return_empty_user_when_no_realm() throws Exception {
+ assertThat(underTest.authenticate(LOGIN, PASSWORD, request)).isEmpty();
+ }
+
+ @Test
+ public void fail_to_start_when_no_authenticator() throws Exception {
+ when(realm.doGetAuthenticator()).thenReturn(null);
+ when(securityRealmFactory.getRealm()).thenReturn(realm);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("No authenticator available");
+ underTest.start();
+ }
+
+ @Test
+ public void fail_to_start_when_no_user_provider() throws Exception {
+ when(realm.doGetAuthenticator()).thenReturn(authenticator);
+ when(realm.getUsersProvider()).thenReturn(null);
+ when(securityRealmFactory.getRealm()).thenReturn(realm);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("No users provider available");
+ underTest.start();
+ }
+
+ private void executeStartWithoutGroupSync() {
+ when(realm.doGetAuthenticator()).thenReturn(authenticator);
+ when(realm.getUsersProvider()).thenReturn(externalUsersProvider);
+ when(securityRealmFactory.getRealm()).thenReturn(realm);
+ underTest.start();
+ }
+
+ private void executeStartWithGroupSync() {
+ when(realm.doGetAuthenticator()).thenReturn(authenticator);
+ when(realm.getUsersProvider()).thenReturn(externalUsersProvider);
+ when(realm.getGroupsProvider()).thenReturn(externalGroupsProvider);
+ when(securityRealmFactory.getRealm()).thenReturn(realm);
+ underTest.start();
+ }
+
+ private void executeAuthenticate() {
+ executeAuthenticate(LOGIN);
+ }
+
+ private void executeAuthenticate(String login) {
+ when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(true);
+ UserDetails userDetails = new UserDetails();
+ userDetails.setName("name");
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(userDetails);
+ underTest.authenticate(login, PASSWORD, request);
+ }
+
+}
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
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;
UserDao userDao = dbClient.userDao();
GroupDao groupDao = dbClient.groupDao();
Settings settings = new Settings();
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
- HttpSession httpSession = mock(HttpSession.class);
UserUpdater userUpdater = new UserUpdater(
mock(NewUserNotifier.class),
settings,
dbClient,
mock(UserIndexer.class),
- system2
- );
+ system2);
- UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(dbClient, userUpdater, jwtHttpHandler);
+ UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(dbClient, userUpdater);
@Before
public void setUp() throws Exception {
settings.setProperty("sonar.defaultGroup", DEFAULT_GROUP);
addGroup(DEFAULT_GROUP);
- when(request.getSession()).thenReturn(httpSession);
}
@Test
public void authenticate_new_user() throws Exception {
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
dbSession.commit();
UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
.setName("John")
// group3 doesn't exist in db, it will be ignored
.setGroups(newHashSet("group1", "group2", "group3"))
- .build(), IDENTITY_PROVIDER, request, response);
+ .build(), IDENTITY_PROVIDER);
dbSession.commit();
UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
.setName("Old name")
.setEmail("Old email")
.setExternalIdentity("old identity")
- .setExternalIdentityProvider("old provide")
- );
+ .setExternalIdentityProvider("old provide"));
dbSession.commit();
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
dbSession.commit();
UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
.setName("Old name")
.setEmail("Old email")
.setExternalIdentity("old identity")
- .setExternalIdentityProvider("old provide")
- );
+ .setExternalIdentityProvider("old provide"));
dbSession.commit();
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
dbSession.commit();
UserDto userDto = userDao.selectByLogin(dbSession, USER_LOGIN);
userDao.insert(dbSession, new UserDto()
.setLogin(USER_LOGIN)
.setActive(true)
- .setName("John")
- );
+ .setName("John"));
addGroup("group1");
addGroup("group2");
dbSession.commit();
.setName("John")
// group3 doesn't exist in db, it will be ignored
.setGroups(newHashSet("group1", "group2", "group3"))
- .build(), IDENTITY_PROVIDER, request, response);
+ .build(), IDENTITY_PROVIDER);
dbSession.commit();
Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(USER_LOGIN)).get(USER_LOGIN));
.setName("John")
// Only group1 is returned by the id provider => group2 will be removed
.setGroups(newHashSet("group1"))
- .build(), IDENTITY_PROVIDER, request, response);
+ .build(), IDENTITY_PROVIDER);
dbSession.commit();
verifyUserGroups(USER_LOGIN, "group1");
.setName("John")
// No group => group1 and group2 will be removed
.setGroups(Collections.<String>emptySet())
- .build(), IDENTITY_PROVIDER, request, response);
+ .build(), IDENTITY_PROVIDER);
dbSession.commit();
verifyNoUserGroups(USER_LOGIN);
}
- @Test
- public void update_session_for_rails() throws Exception {
- UserDto userDto = UserTesting.newUserDto().setLogin(USER_LOGIN);
- userDao.insert(dbSession, userDto);
- dbSession.commit();
-
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
-
- verify(httpSession).setAttribute("user_id", userDto.getId());
- }
-
- @Test
- public void create_jwt_token() throws Exception {
- UserDto userDto = UserTesting.newUserDto().setLogin(USER_LOGIN);
- userDao.insert(dbSession, userDto);
- dbSession.commit();
-
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
-
- verify(httpSession).setAttribute("user_id", userDto.getId());
- verify(jwtHttpHandler).generateToken(USER_LOGIN, response);
- }
-
@Test
public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() throws Exception {
TestIdentityProvider identityProvider = new TestIdentityProvider()
thrown.expect(UnauthorizedException.class);
thrown.expectMessage("'github' users are not allowed to sign up");
- underTest.authenticate(USER_IDENTITY, identityProvider, request, response);
+ underTest.authenticate(USER_IDENTITY, identityProvider);
}
@Test
thrown.expect(UnauthorizedException.class);
thrown.expectMessage("You can't sign up because email 'john@email.com' is already used by an existing user. " +
"This means that you probably already registered with another account.");
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, request, response);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
}
private void verifyUserGroups(String userLogin, String... groups) {
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);
- ValidateJwtTokenFilter underTest = new ValidateJwtTokenFilter(jwtHttpHandler);
+ 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("/test");
+ when(request.getRequestURI()).thenReturn("/measures");
}
@Test
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();
@Test
public void validate_session() throws Exception {
+ userSession.login("john");
underTest.doFilter(request, response, chain);
verify(jwtHttpHandler).validateToken(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);
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);
+ }
}
*/
package org.sonar.server.authentication.ws;
+import static org.assertj.core.api.Assertions.assertThat;
+
import org.junit.Test;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
+import org.sonar.server.ws.ServletFilterHandler;
import org.sonar.server.ws.WsTester;
-import static org.assertj.core.api.Assertions.assertThat;
-
public class AuthenticationWsTest {
WsTester tester = new WsTester(new AuthenticationWs());
WebService.Controller controller = tester.controller("api/authentication");
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
- assertThat(controller.actions()).hasSize(1);
+ assertThat(controller.actions()).hasSize(2);
WebService.Action validate = controller.action("validate");
assertThat(validate).isNotNull();
assertThat(validate.handler()).isInstanceOf(RailsHandler.class);
assertThat(validate.responseExampleAsString()).isNotEmpty();
assertThat(validate.params()).hasSize(1);
+
+ WebService.Action login = controller.action("login");
+ assertThat(login).isNotNull();
+ assertThat(login.handler()).isInstanceOf(ServletFilterHandler.class);
+ assertThat(login.isPost()).isTrue();
+ assertThat(login.params()).hasSize(2);
}
}
+++ /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.user;
-
-import java.util.Locale;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.core.platform.ComponentContainer;
-import org.sonar.server.platform.Platform;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class RubyUserSessionTest {
- Platform platform = mock(Platform.class);
- ComponentContainer componentContainer = mock(ComponentContainer.class);
- ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
- RubyUserSession underTest = new RubyUserSession(platform);
-
- @Before
- public void setUp() {
- // for test isolation
- threadLocalUserSession.remove();
-
- when(platform.getContainer()).thenReturn(componentContainer);
- when(componentContainer.getComponentByType(ThreadLocalUserSession.class)).thenReturn(threadLocalUserSession);
- }
-
- @After
- public void tearDown() {
- // clean up for next test
- threadLocalUserSession.remove();
- }
-
- @Test
- public void should_set_session() {
- underTest.setSessionImpl(123, "karadoc", "Karadoc", newArrayList("sonar-users"), "fr");
-
- UserSession session = threadLocalUserSession.get();
-
- assertThat(session).isNotNull();
- assertThat(session.getLogin()).isEqualTo("karadoc");
- assertThat(session.getName()).isEqualTo("Karadoc");
- assertThat(session.getUserId()).isEqualTo(123);
- assertThat(session.getUserGroups()).containsOnly("sonar-users", "Anyone");
- assertThat(session.isLoggedIn()).isTrue();
- assertThat(session.locale()).isEqualTo(Locale.FRENCH);
- }
-
- @Test
- public void should_set_anonymous_session() {
- underTest.setSessionImpl(null, null, null, null, "fr");
-
- UserSession session = threadLocalUserSession.get();
-
- assertThat(session).isNotNull();
- assertThat(session.getLogin()).isNull();
- assertThat(session.getName()).isNull();
- assertThat(session.getUserId()).isNull();
- assertThat(session.getUserGroups()).containsOnly("Anyone");
- assertThat(session.isLoggedIn()).isFalse();
- assertThat(session.locale()).isEqualTo(Locale.FRENCH);
- }
-
-}
*/
package org.sonar.server.user;
-import java.util.Arrays;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.DASHBOARD_SHARING;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_PROFILE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.user.ServerUserSession.createForAnonymous;
+import static org.sonar.server.user.ServerUserSession.createForUser;
+
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
-import org.sonar.db.component.ResourceDao;
-import org.sonar.db.component.ResourceDto;
-import org.sonar.db.user.AuthorizationDao;
+import org.sonar.db.user.GroupRoleDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserRoleDto;
import org.sonar.server.exceptions.ForbiddenException;
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
public class ServerUserSessionTest {
static final String LOGIN = "marius";
- static final String PROJECT_KEY = "com.foo:Bar";
+
static final String PROJECT_UUID = "ABCD";
static final String FILE_KEY = "com.foo:Bar:BarFile.xoo";
static final String FILE_UUID = "BCDE";
- AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
- ResourceDao resourceDao = mock(ResourceDao.class);
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
- @Test
- public void login_should_not_be_empty() {
- UserSession session = newServerUserSession().setLogin("");
- assertThat(session.getLogin()).isNull();
- assertThat(session.isLoggedIn()).isFalse();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ ComponentDbTester componentDbTester = new ComponentDbTester(dbTester);
+
+ DbClient dbClient = dbTester.getDbClient();
+
+ DbSession dbSession = dbTester.getSession();
+
+ UserDto userDto = newUserDto().setLogin(LOGIN);
+ ComponentDto project, file;
+
+ @Before
+ public void setUp() throws Exception {
+ project = componentDbTester.insertComponent(ComponentTesting.newProjectDto(PROJECT_UUID));
+ file = componentDbTester.insertComponent(ComponentTesting.newFileDto(project, FILE_UUID).setKey(FILE_KEY));
+ dbClient.userDao().insert(dbSession, userDto);
+ dbSession.commit();
}
@Test
public void has_global_permission() {
- UserSession session = newServerUserSession().setLogin(LOGIN);
+ addGlobalPermissions("admin", "profileadmin");
+ UserSession session = newUserSession(userDto);
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList("profileadmin", "admin"));
-
- assertThat(session.hasPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN)).isTrue();
- assertThat(session.hasPermission(GlobalPermissions.SYSTEM_ADMIN)).isTrue();
- assertThat(session.hasPermission(GlobalPermissions.DASHBOARD_SHARING)).isFalse();
+ assertThat(session.hasPermission(QUALITY_PROFILE_ADMIN)).isTrue();
+ assertThat(session.hasPermission(SYSTEM_ADMIN)).isTrue();
+ assertThat(session.hasPermission(DASHBOARD_SHARING)).isFalse();
}
@Test
public void check_global_Permission_ok() {
- UserSession session = newServerUserSession().setLogin(LOGIN);
+ addGlobalPermissions("admin", "profileadmin");
+ UserSession session = newUserSession(userDto);
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList("profileadmin", "admin"));
-
- session.checkPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+ session.checkPermission(QUALITY_PROFILE_ADMIN);
}
- @Test(expected = ForbiddenException.class)
+ @Test
public void check_global_Permission_ko() {
- UserSession session = newServerUserSession().setLogin(LOGIN);
+ addGlobalPermissions("admin", "profileadmin");
+ UserSession session = newUserSession(userDto);
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList("profileadmin", "admin"));
-
- session.checkPermission(GlobalPermissions.DASHBOARD_SHARING);
+ expectedException.expect(ForbiddenException.class);
+ session.checkPermission(DASHBOARD_SHARING);
}
@Test
public void has_component_permission() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- String componentKey = FILE_KEY;
- when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(componentKey));
- when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(componentKey));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
- assertThat(session.hasComponentPermission(UserRole.USER, componentKey)).isTrue();
- assertThat(session.hasComponentPermission(UserRole.CODEVIEWER, componentKey)).isFalse();
- assertThat(session.hasComponentPermission(UserRole.ADMIN, componentKey)).isFalse();
+ assertThat(session.hasComponentPermission(UserRole.USER, FILE_KEY)).isTrue();
+ assertThat(session.hasComponentPermission(UserRole.CODEVIEWER, FILE_KEY)).isFalse();
+ assertThat(session.hasComponentPermission(UserRole.ADMIN, FILE_KEY)).isFalse();
}
@Test
public void has_component_uuid_permission() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- String componentUuid = FILE_UUID;
- when(resourceDao.selectResource(componentUuid)).thenReturn(new ResourceDto().setUuid(componentUuid).setProjectUuid(PROJECT_UUID));
- when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(PROJECT_UUID));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
- assertThat(session.hasComponentUuidPermission(UserRole.USER, componentUuid)).isTrue();
- assertThat(session.hasComponentUuidPermission(UserRole.CODEVIEWER, componentUuid)).isFalse();
- assertThat(session.hasComponentUuidPermission(UserRole.ADMIN, componentUuid)).isFalse();
+ assertThat(session.hasComponentUuidPermission(UserRole.USER, FILE_UUID)).isTrue();
+ assertThat(session.hasComponentUuidPermission(UserRole.CODEVIEWER, FILE_UUID)).isFalse();
+ assertThat(session.hasComponentUuidPermission(UserRole.ADMIN, FILE_UUID)).isFalse();
}
@Test
public void has_component_permission_with_only_global_permission() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
+ addGlobalPermissions(UserRole.USER);
+ UserSession session = newUserSession(userDto);
- String componentKey = FILE_KEY;
- when(resourceDao.getRootProjectByComponentKey(componentKey)).thenReturn(new ResourceDto().setKey(componentKey));
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList(UserRole.USER));
-
- assertThat(session.hasComponentPermission(UserRole.USER, componentKey)).isTrue();
- assertThat(session.hasComponentPermission(UserRole.CODEVIEWER, componentKey)).isFalse();
- assertThat(session.hasComponentPermission(UserRole.ADMIN, componentKey)).isFalse();
+ assertThat(session.hasComponentPermission(UserRole.USER, FILE_KEY)).isTrue();
+ assertThat(session.hasComponentPermission(UserRole.CODEVIEWER, FILE_KEY)).isFalse();
+ assertThat(session.hasComponentPermission(UserRole.ADMIN, FILE_KEY)).isFalse();
}
@Test
public void has_component_uuid_permission_with_only_global_permission() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- String componentUuid = FILE_UUID;
- when(resourceDao.selectResource(componentUuid)).thenReturn(new ResourceDto().setUuid(componentUuid).setProjectUuid(PROJECT_UUID));
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList(UserRole.USER));
+ addGlobalPermissions(UserRole.USER);
+ UserSession session = newUserSession(userDto);
- assertThat(session.hasComponentUuidPermission(UserRole.USER, componentUuid)).isTrue();
- assertThat(session.hasComponentUuidPermission(UserRole.CODEVIEWER, componentUuid)).isFalse();
- assertThat(session.hasComponentUuidPermission(UserRole.ADMIN, componentUuid)).isFalse();
+ assertThat(session.hasComponentUuidPermission(UserRole.USER, FILE_UUID)).isTrue();
+ assertThat(session.hasComponentUuidPermission(UserRole.CODEVIEWER, FILE_UUID)).isFalse();
+ assertThat(session.hasComponentUuidPermission(UserRole.ADMIN, FILE_UUID)).isFalse();
}
@Test
public void check_component_key_permission_ok() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- when(resourceDao.getRootProjectByComponentKey(FILE_KEY)).thenReturn(new ResourceDto().setKey(PROJECT_KEY));
- when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(PROJECT_KEY));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
session.checkComponentPermission(UserRole.USER, FILE_KEY);
}
@Test
public void check_component_key_permission_with_only_global_permission_ok() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- when(resourceDao.getRootProjectByComponentKey(FILE_KEY)).thenReturn(new ResourceDto().setKey(PROJECT_KEY));
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList(UserRole.USER));
+ addGlobalPermissions(UserRole.USER);
+ UserSession session = newUserSession(userDto);
session.checkComponentPermission(UserRole.USER, FILE_KEY);
}
- @Test(expected = ForbiddenException.class)
+ @Test
public void check_component_key_permission_ko() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- when(resourceDao.getRootProjectByComponentKey(FILE_KEY)).thenReturn(new ResourceDto().setKey("com.foo:Bar2"));
- when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(PROJECT_KEY));
+ ComponentDto project2 = componentDbTester.insertComponent(ComponentTesting.newProjectDto());
+ ComponentDto file2 = componentDbTester.insertComponent(ComponentTesting.newFileDto(project2));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
- session.checkComponentPermission(UserRole.USER, FILE_KEY);
+ expectedException.expect(ForbiddenException.class);
+ session.checkComponentPermission(UserRole.USER, file2.getKey());
}
@Test
public void check_component_uuid_permission_ok() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- ComponentDto project = ComponentTesting.newProjectDto();
- ComponentDto file = ComponentTesting.newFileDto(project, "file-uuid");
- when(resourceDao.selectResource("file-uuid")).thenReturn(new ResourceDto().setProjectUuid(project.uuid()));
- when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
- session.checkComponentUuidPermission(UserRole.USER, file.uuid());
+ session.checkComponentUuidPermission(UserRole.USER, FILE_UUID);
}
- @Test(expected = ForbiddenException.class)
+ @Test
public void check_component_uuid_permission_ko() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- ComponentDto project = ComponentTesting.newProjectDto();
- when(resourceDao.selectResource("file-uuid")).thenReturn(new ResourceDto().setProjectUuid(project.uuid()));
- when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
+ expectedException.expect(ForbiddenException.class);
session.checkComponentUuidPermission(UserRole.USER, "another-uuid");
}
- @Test(expected = ForbiddenException.class)
+ @Test
public void check_component_key_permission_when_project_not_found() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- when(resourceDao.getRootProjectByComponentKey(FILE_KEY)).thenReturn(null);
-
- session.checkComponentPermission(UserRole.USER, FILE_KEY);
+ ComponentDto project2 = componentDbTester.insertComponent(ComponentTesting.newProjectDto());
+ ComponentDto file2 = componentDbTester.insertComponent(ComponentTesting.newFileDto(project2)
+ // Simulate file is linked to an invalid project
+ .setProjectUuid("INVALID"));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
+
+ expectedException.expect(ForbiddenException.class);
+ session.checkComponentPermission(UserRole.USER, file2.getKey());
}
- @Test(expected = ForbiddenException.class)
+ @Test
public void check_component_dto_permission_ko() {
- UserSession session = newServerUserSession().setLogin(LOGIN).setUserId(1);
-
- ComponentDto project = ComponentTesting.newProjectDto();
- when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+ addProjectPermissions(project, UserRole.USER);
+ UserSession session = newUserSession(userDto);
+ expectedException.expect(ForbiddenException.class);
session.checkComponentPermission(UserRole.USER, "another");
}
@Test
public void deprecated_has_global_permission() throws Exception {
- UserSession session = newServerUserSession().setLogin(LOGIN);
-
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList("profileadmin", "admin"));
+ addGlobalPermissions("profileadmin", "admin");
+ UserSession session = newUserSession(userDto);
- assertThat(session.hasGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN)).isTrue();
- assertThat(session.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)).isTrue();
- assertThat(session.hasGlobalPermission(GlobalPermissions.DASHBOARD_SHARING)).isFalse();
+ assertThat(session.hasGlobalPermission(QUALITY_PROFILE_ADMIN)).isTrue();
+ assertThat(session.hasGlobalPermission(SYSTEM_ADMIN)).isTrue();
+ assertThat(session.hasGlobalPermission(DASHBOARD_SHARING)).isFalse();
}
@Test
public void deprecated_check_global_permission() throws Exception {
- UserSession session = newServerUserSession().setLogin(LOGIN);
+ addGlobalPermissions("profileadmin", "admin");
+ UserSession session = newUserSession(userDto);
- when(authorizationDao.selectGlobalPermissions(LOGIN)).thenReturn(Arrays.asList("profileadmin", "admin"));
+ session.checkGlobalPermission(QUALITY_PROFILE_ADMIN);
+ }
- session.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN);
+ @Test
+ public void fail_if_user_dto_is_null() throws Exception {
+ expectedException.expect(NullPointerException.class);
+ newUserSession(null);
+ }
+
+ @Test
+ public void anonymous_user() throws Exception {
+ UserSession session = newAnonymousSession();
+
+ assertThat(session.getLogin()).isNull();
+ assertThat(session.isLoggedIn()).isFalse();
}
- private ServerUserSession newServerUserSession() {
- return new ServerUserSession(authorizationDao, resourceDao);
+ @Test
+ public void has_global_permission_for_anonymous() throws Exception {
+ addAnonymousPermissions(null, "profileadmin", "admin");
+ UserSession session = newAnonymousSession();
+
+ assertThat(session.getLogin()).isNull();
+ assertThat(session.isLoggedIn()).isFalse();
+
+ assertThat(session.hasPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN)).isTrue();
+ assertThat(session.hasPermission(GlobalPermissions.SYSTEM_ADMIN)).isTrue();
+ assertThat(session.hasPermission(GlobalPermissions.DASHBOARD_SHARING)).isFalse();
}
+ @Test
+ public void has_project_permission_for_anonymous() throws Exception {
+ addAnonymousPermissions(project, UserRole.USER);
+ UserSession session = newAnonymousSession();
+
+ assertThat(session.hasComponentPermission(UserRole.USER, FILE_KEY)).isTrue();
+ assertThat(session.hasComponentPermission(UserRole.CODEVIEWER, FILE_KEY)).isFalse();
+ assertThat(session.hasComponentPermission(UserRole.ADMIN, FILE_KEY)).isFalse();
+ }
+
+ private ServerUserSession newUserSession(UserDto userDto) {
+ return createForUser(dbClient, userDto);
+ }
+
+ private ServerUserSession newAnonymousSession() {
+ return createForAnonymous(dbClient);
+ }
+
+ private void addGlobalPermissions(String... permissions) {
+ addPermissions(null, permissions);
+ }
+
+ private void addProjectPermissions(ComponentDto component, String... permissions) {
+ addPermissions(component, permissions);
+ }
+
+ private void addPermissions( @Nullable ComponentDto component, String... permissions) {
+ for (String permission : permissions) {
+ dbClient.roleDao().insertUserRole(dbSession, new UserRoleDto()
+ .setRole(permission)
+ .setResourceId(component == null ? null : component.getId())
+ .setUserId(userDto.getId()));
+ }
+ dbSession.commit();
+ }
+
+ private void addAnonymousPermissions(@Nullable ComponentDto component, String... permissions) {
+ for (String permission : permissions) {
+ dbClient.roleDao().insertGroupRole(dbSession, new GroupRoleDto()
+ .setRole(permission)
+ .setResourceId(component == null ? null : component.getId()));
+ }
+ dbSession.commit();
+ }
+
+
}
*/
package org.sonar.server.user;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.data.MapEntry.entry;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.CoreProperties.CORE_DEFAULT_GROUP;
+import static org.sonar.db.user.UserTesting.newDisabledUser;
+import static org.sonar.db.user.UserTesting.newUserDto;
+
import com.google.common.base.Strings;
import java.util.List;
import org.elasticsearch.search.SearchHit;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.util.Validation;
-import static com.google.common.collect.Lists.newArrayList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.data.MapEntry.entry;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.CoreProperties.CORE_DEFAULT_GROUP;
-import static org.sonar.db.user.UserTesting.newDisabledUser;
-import static org.sonar.db.user.UserTesting.newUserDto;
-
public class UserUpdaterTest {
static final long NOW = 1418215735482L;
.setLogin("user")
.setName("User")
.setEmail("user@mail.com")
- .setPassword("password")
+ .setPassword("PASSWORD")
.setScmAccounts(newArrayList("u1", "u_1", "User 1")));
UserDto dto = userDao.selectByLogin(session, "user");
assertThat(dto.getScmAccounts()).isNull();
assertThat(dto.isLocal()).isTrue();
- assertThat(dto.getSalt()).isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
- assertThat(dto.getCryptedPassword()).isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
+ assertThat(dto.getSalt()).isNotNull().isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
+ assertThat(dto.getCryptedPassword()).isNotNull().isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
assertThat(dto.getCreatedAt()).isEqualTo(PAST);
assertThat(dto.getUpdatedAt()).isEqualTo(NOW);
end
def anonymous?
- !session.has_key?('user_id')
+ current_user.nil?
end
def set_cache_buster
include AuthenticatedSystem
include NeedAuthorization::Helper
- before_filter :check_database_version, :set_user_session, :check_authentication
+ before_filter :check_database_version, :set_i18n, :check_authentication
# Required for JRuby 1.7
rescue_from 'Java::JavaLang::Exception', :with => :render_java_exception
end
end
- def set_user_session
+ def set_i18n
+ # TODO Is it really needed to do this ?
if params[:locale]
I18n.locale = request.compatible_language_from(available_locales, [params[:locale]])
else
I18n.locale = request.compatible_language_from(available_locales)
end
-
- if current_user && current_user.id
- user_groups_name = current_user.groups.collect {|g| g.name}.to_a
- Java::OrgSonarServerUser::RubyUserSession.setSession(current_user.id.to_i, current_user.login, current_user.name, user_groups_name, I18n.locale.to_s)
- else
- Java::OrgSonarServerUser::RubyUserSession.setSession(nil, nil, nil, nil, I18n.locale.to_s)
- end
end
def check_authentication
# 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_session || login_from_basic_auth) unless @current_user == false
+ @current_user ||= (login_from_java_user_session || login_from_basic_auth) unless @current_user == false
end
- # Store the given user id in the session.
+ # Store the given user
def current_user=(new_user)
if new_user
- session['user_id'] = new_user.id
@current_user = new_user
else
- session['user_id'] = nil
@current_user = false
end
end
#
# Called from #current_user. First attempt to login by the user id stored in the session.
- def login_from_session
- self.current_user = User.find_by_id(session['user_id']) if session['user_id']
+ def login_from_java_user_session
+ userSession = Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerUser::UserSession.java_class)
+ user_id = userSession.getUserId() if userSession && userSession.isLoggedIn()
+ 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.
*/
package org.sonar.db.user;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.core.user.DefaultUser;
return this;
}
+ @CheckForNull
public String getCryptedPassword() {
return cryptedPassword;
}
- public UserDto setCryptedPassword(String cryptedPassword) {
+ public UserDto setCryptedPassword(@Nullable String cryptedPassword) {
this.cryptedPassword = cryptedPassword;
return this;
}
+ @CheckForNull
public String getSalt() {
return salt;
}
- public UserDto setSalt(String salt) {
+ public UserDto setSalt(@Nullable String salt) {
this.salt = salt;
return this;
}
return this;
}
+ public static String encryptPassword(String password, String salt) {
+ requireNonNull(password, "Password cannot be empty");
+ requireNonNull(salt, "Salt cannot be empty");
+ return DigestUtils.sha1Hex("--" + salt + "--" + password + "--");
+ }
+
public DefaultUser toUser() {
return new DefaultUser()
.setLogin(login)
*/
package org.sonar.db.user;
+import static org.assertj.core.api.Assertions.assertThat;
+
import java.util.Arrays;
import java.util.Collections;
+import org.junit.Rule;
import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.rules.ExpectedException;
public class UserDtoTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Test
public void encode_scm_accounts() {
assertThat(UserDto.encodeScmAccounts(null)).isNull();
assertThat(UserDto.decodeScmAccounts("\nfoo\n")).containsOnly("foo");
assertThat(UserDto.decodeScmAccounts("\nfoo\nbar\n")).containsOnly("foo", "bar");
}
+
+ @Test
+ public void encrypt_password() throws Exception {
+ assertThat(UserDto.encryptPassword("PASSWORD", "0242b0b4c0a93ddfe09dd886de50bc25ba000b51")).isEqualTo("540e4fc4be4e047db995bc76d18374a5b5db08cc");
+ }
+
+ @Test
+ public void fail_to_encrypt_password_when_password_is_null() throws Exception {
+ expectedException.expect(NullPointerException.class);
+ UserDto.encryptPassword(null, "salt");
+ }
+
+ @Test
+ public void fail_to_encrypt_password_when_salt_is_null() throws Exception {
+ expectedException.expect(NullPointerException.class);
+ UserDto.encryptPassword("password", null);
+ }
}
*/
package org.sonar.api.server.authentication;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.FluentIterable.from;
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.sonar.api.user.UserGroupValidation.validateGroupName;
+
import com.google.common.base.Predicate;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.sonar.api.CoreProperties;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.FluentIterable.from;
-import static org.apache.commons.lang.StringUtils.isNotBlank;
-import static org.sonar.api.user.UserGroupValidation.validateGroupName;
-
/**
* User information provided by the Identity Provider to be register into the platform.
*
* @since 5.5
*/
public Builder setGroups(Set<String> groups) {
- checkNotNull(groups, "Groups cannot be null, please don't this method if groups should not be synchronized.");
+ checkNotNull(groups, "Groups cannot be null, please don't use this method if groups should not be synchronized.");
from(groups).filter(ValidateGroupName.INSTANCE).toList();
this.groupsProvided = true;
this.groups = groups;
*/
package org.sonar.api.server.authentication;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.assertj.core.api.Assertions.assertThat;
+
import com.google.common.base.Strings;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import static com.google.common.collect.Sets.newHashSet;
-import static org.assertj.core.api.Assertions.assertThat;
-
public class UserIdentityTest {
@Rule
@Test
public void fail_when_groups_are_null() throws Exception {
thrown.expect(NullPointerException.class);
- thrown.expectMessage("Groups cannot be null, please don't this method if groups should not be synchronized.");
+ thrown.expectMessage("Groups cannot be null, please don't use this method if groups should not be synchronized.");
UserIdentity.builder()
.setProviderLogin("john")