generic UnauthorizedException thrown in case of login failure is replaced by specific AuthenticationException which includes context information to generate details failure logs
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+
public class BaseContextFactory {
private final DbClient dbClient;
@Override
public void authenticate(UserIdentity userIdentity) {
- UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
+ UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider, Source.external(identityProvider));
jwtHttpHandler.generateToken(userDto, request, response);
threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.usertoken.UserTokenAuthenticator;
import static java.util.Locale.ENGLISH;
int semiColonPos = basicAuthDecoded.indexOf(':');
if (semiColonPos <= 0) {
- throw new UnauthorizedException("Invalid credentials : " + basicAuthDecoded);
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.BASIC))
+ .setMessage("decoded basic auth does not contain ':'")
+ .build();
}
String login = basicAuthDecoded.substring(0, semiColonPos);
String password = basicAuthDecoded.substring(semiColonPos + 1);
try {
return new String(BASE64_DECODER.decode(basicAuthEncoded.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
} catch (Exception e) {
- throw new UnauthorizedException("Invalid basic header");
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.BASIC))
+ .setMessage("Invalid basic header")
+ .build();
}
}
private UserDto authenticateFromUserToken(String token) {
Optional<String> authenticatedLogin = userTokenAuthenticator.authenticate(token);
if (!authenticatedLogin.isPresent()) {
- throw new UnauthorizedException("Token doesn't exist");
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.BASIC_TOKEN))
+ .setMessage("Token doesn't exist")
+ .build();
}
- DbSession dbSession = dbClient.openSession(false);
- try {
+ try (DbSession dbSession = dbClient.openSession(false)) {
UserDto userDto = dbClient.userDao().selectActiveUserByLogin(dbSession, authenticatedLogin.get());
if (userDto == null) {
- throw new UnauthorizedException("User doesn't exist");
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.BASIC_TOKEN))
+ .setMessage("User doesn't exist")
+ .build();
}
return userDto;
- } finally {
- dbClient.closeSession(dbSession);
}
}
package org.sonar.server.authentication;
import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
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.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import static org.sonar.db.user.UserDto.encryptPassword;
-import static org.sonar.server.authentication.event.AuthenticationEvent.*;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class CredentialsAuthenticator {
}
private UserDto authenticate(DbSession dbSession, String userLogin, String userPassword, HttpServletRequest request, Method method) {
- UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin);
- if (user != null && user.isLocal()) {
- UserDto userDto = authenticateFromDb(user, userPassword);
+ UserDto localUser = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin);
+ if (localUser != null && localUser.isLocal()) {
+ UserDto userDto = authenticateFromDb(localUser, userPassword, method);
authenticationEvent.login(request, userLogin, Source.local(method));
return userDto;
}
- Optional<UserDto> userDto = externalAuthenticator.authenticate(userLogin, userPassword, request, method);
- if (userDto.isPresent()) {
- return userDto.get();
+ Optional<UserDto> externalUser = externalAuthenticator.authenticate(userLogin, userPassword, request, method);
+ if (externalUser.isPresent()) {
+ return externalUser.get();
}
- throw new UnauthorizedException();
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(method))
+ .setLogin(userLogin)
+ .setMessage(localUser != null && !localUser.isLocal() ? "User is not local" : "No active user for login")
+ .build();
}
- private static UserDto authenticateFromDb(UserDto userDto, String userPassword) {
+ private static UserDto authenticateFromDb(UserDto userDto, String userPassword, Method method) {
String cryptedPassword = userDto.getCryptedPassword();
String salt = userDto.getSalt();
- if (cryptedPassword == null || salt == null
- || !cryptedPassword.equals(encryptPassword(userPassword, salt))) {
- throw new UnauthorizedException();
+ String failureCause = checkPassword(cryptedPassword, salt, userPassword);
+ if (failureCause == null) {
+ return userDto;
}
- return userDto;
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(method))
+ .setLogin(userDto.getLogin())
+ .setMessage(failureCause)
+ .build();
}
+ @CheckForNull
+ private static String checkPassword(@Nullable String cryptedPassword, @Nullable String salt, String userPassword) {
+ if (cryptedPassword == null) {
+ return "null password in DB";
+ } else if (salt == null) {
+ return "null salt";
+ } else if (!cryptedPassword.equals(encryptPassword(userPassword, salt))) {
+ return "wrong password";
+ }
+ return null;
+ }
}
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Set;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.sonar.server.authentication.CookieUtils.createCookie;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class JwtCsrfVerifier {
return state;
}
- public void verifyState(HttpServletRequest request, @Nullable String csrfState) {
+ public void verifyState(HttpServletRequest request, @Nullable String csrfState, @Nullable String login) {
if (!shouldRequestBeChecked(request)) {
return;
}
- String stateInHeader = request.getHeader(CSRF_HEADER);
- if (isBlank(csrfState) || !StringUtils.equals(csrfState, stateInHeader)) {
- throw new UnauthorizedException();
+
+ String failureCause = checkCsrf(csrfState, request.getHeader(CSRF_HEADER));
+ if (failureCause != null) {
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.JWT))
+ .setLogin(login)
+ .setMessage(failureCause)
+ .build();
+ }
+ }
+
+ @CheckForNull
+ private static String checkCsrf(@Nullable String csrfState, @Nullable String stateInHeader) {
+ if (isBlank(csrfState)) {
+ return "missing reference CSRF value";
+ }
+ if (!StringUtils.equals(csrfState, stateInHeader)) {
+ return "wrong CSFR in request";
}
+ return null;
}
public void refreshState(HttpServletRequest request, HttpServletResponse response, String csrfState, int timeoutInSeconds) {
if (now.after(addSeconds(token.getIssuedAt(), SESSION_DISCONNECT_IN_SECONDS))) {
return Optional.empty();
}
- jwtCsrfVerifier.verifyState(request, (String) token.get(CSRF_JWT_PARAM));
+ jwtCsrfVerifier.verifyState(request, (String) token.get(CSRF_JWT_PARAM), token.getSubject());
if (now.after(addSeconds(getLastRefreshDate(token), SESSION_REFRESH_IN_SECONDS))) {
refreshToken(token, request, response);
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.jsonwebtoken.impl.crypto.MacProvider.generateKey;
import static java.util.Objects.requireNonNull;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
/**
* This class can be used to encode or decode a JWT token
Optional<Claims> decode(String token) {
checkIsStarted();
+ Claims claims = null;
try {
- Claims claims = Jwts.parser()
+ claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException | SignatureException e) {
return Optional.empty();
} catch (Exception e) {
- throw new UnauthorizedException(e.getMessage());
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.jwt())
+ .setLogin(claims == null ? null : claims.getSubject())
+ .setMessage(e.getMessage())
+ .build();
}
}
WrappedContext context = new WrappedContext(oAuth2ContextFactory.newCallback(httpRequest, (HttpServletResponse) response, oauthProvider));
oauthProvider.callback(context);
if (context.isAuthenticated()) {
- authenticationEvent.login(httpRequest, context.getLogin(), Source.oauth2(provider.getName()));
+ authenticationEvent.login(httpRequest, context.getLogin(), Source.oauth2(oauthProvider));
}
} else {
handleError((HttpServletResponse) response, format("Not an OAuth2IdentityProvider: %s", provider.getClass()));
import org.sonar.api.utils.MessageException;
import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
@Override
public void verifyCsrfState() {
- csrfVerifier.verifyState(request, response);
+ csrfVerifier.verifyState(request, response, identityProvider);
}
@Override
@Override
public void authenticate(UserIdentity userIdentity) {
- UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider);
+ UserDto userDto = userIdentityAuthenticator.authenticate(userIdentity, identityProvider, AuthenticationEvent.Source.oauth2(identityProvider));
jwtHttpHandler.generateToken(userDto, request, response);
threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
}
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.api.server.authentication.OAuth2IdentityProvider;
+import org.sonar.server.authentication.event.AuthenticationException;
import static java.lang.String.format;
import static org.apache.commons.codec.digest.DigestUtils.sha256Hex;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.sonar.server.authentication.CookieUtils.createCookie;
import static org.sonar.server.authentication.CookieUtils.findCookie;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class OAuthCsrfVerifier {
return state;
}
- public void verifyState(HttpServletRequest request, HttpServletResponse response) {
- Cookie cookie = findCookie(CSRF_STATE_COOKIE, request).orElseThrow(() -> new UnauthorizedException(format("Cookie '%s' is missing", CSRF_STATE_COOKIE)));
+ public void verifyState(HttpServletRequest request, HttpServletResponse response, OAuth2IdentityProvider provider) {
+ Cookie cookie = findCookie(CSRF_STATE_COOKIE, request)
+ .orElseThrow(() -> AuthenticationException.newBuilder()
+ .setSource(Source.oauth2(provider))
+ .setMessage(format("Cookie '%s' is missing", CSRF_STATE_COOKIE))
+ .build());
String hashInCookie = cookie.getValue();
// remove cookie
String stateInRequest = request.getParameter("state");
if (isBlank(stateInRequest) || !sha256Hex(stateInRequest).equals(hashInCookie)) {
- throw new UnauthorizedException("CSRF state value is invalid");
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.oauth2(provider))
+ .setMessage("CSRF state value is invalid")
+ .build();
}
}
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.user.SecurityRealmFactory;
import static java.util.Objects.requireNonNull;
ExternalUsersProvider.Context externalUsersProviderContext = new ExternalUsersProvider.Context(userLogin, request);
UserDetails details = externalUsersProvider.doGetUserDetails(externalUsersProviderContext);
if (details == null) {
- throw new UnauthorizedException("No user details");
+ throw AuthenticationException.newBuilder()
+ .setSource(realmEventSource(method))
+ .setLogin(userLogin)
+ .setMessage("No user details")
+ .build();
}
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");
+ throw AuthenticationException.newBuilder()
+ .setSource(realmEventSource(method))
+ .setLogin(userLogin)
+ .setMessage("realm returned authenticate=false")
+ .build();
}
- UserDto userDto = synchronize(userLogin, details, request);
- authenticationEvent.login(request, userLogin, Source.realm(method, realm.getName()));
+ UserDto userDto = synchronize(userLogin, details, request, method);
+ authenticationEvent.login(request, userLogin, realmEventSource(method));
return userDto;
+ } catch (AuthenticationException e) {
+ throw e;
} 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();
+ throw AuthenticationException.newBuilder()
+ .setSource(realmEventSource(method))
+ .setLogin(userLogin)
+ .setMessage(e.getMessage())
+ .build();
}
}
- private UserDto synchronize(String userLogin, UserDetails details, HttpServletRequest request) {
+ private Source realmEventSource(AuthenticationEvent.Method method) {
+ return Source.realm(method, realm.getName());
+ }
+
+ private UserDto synchronize(String userLogin, UserDetails details, HttpServletRequest request, AuthenticationEvent.Method method) {
String name = details.getName();
UserIdentity.Builder userIdentityBuilder = UserIdentity.builder()
.setLogin(userLogin)
Collection<String> groups = externalGroupsProvider.doGetGroups(context);
userIdentityBuilder.setGroups(new HashSet<>(groups));
}
- return userIdentityAuthenticator.authenticate(userIdentityBuilder.build(), new ExternalIdentityProvider());
+ return userIdentityAuthenticator.authenticate(userIdentityBuilder.build(), new ExternalIdentityProvider(), realmEventSource(method));
}
private String getLogin(String userLogin) {
String groupsValue = getHeaderValue(headerValuesByNames, GROUPS_HEADER_PARAM);
userIdentityBuilder.setGroups(groupsValue == null ? Collections.emptySet() : new HashSet<>(COMA_SPLITTER.splitToList(groupsValue)));
}
- return userIdentityAuthenticator.authenticate(userIdentityBuilder.build(), new SsoIdentityProvider());
+ return userIdentityAuthenticator.authenticate(userIdentityBuilder.build(), new SsoIdentityProvider(), Source.sso());
}
@CheckForNull
import java.util.Set;
import javax.annotation.Nonnull;
import org.sonar.api.server.authentication.IdentityProvider;
-import org.sonar.api.server.authentication.UnauthorizedException;
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.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserGroupDto;
+import org.sonar.server.authentication.event.AuthenticationEvent;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.organization.DefaultOrganization;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.ExternalIdentity;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}
- public UserDto authenticate(UserIdentity user, IdentityProvider provider) {
- return register(user, provider);
+ public UserDto authenticate(UserIdentity user, IdentityProvider provider, AuthenticationEvent.Source source) {
+ return register(user, provider, source);
}
- private UserDto register(UserIdentity user, IdentityProvider provider) {
+ private UserDto register(UserIdentity user, IdentityProvider provider, AuthenticationEvent.Source source) {
DbSession dbSession = dbClient.openSession(false);
try {
String userLogin = user.getLogin();
registerExistingUser(dbSession, userDto, user, provider);
return userDto;
}
- return registerNewUser(dbSession, user, provider);
+ return registerNewUser(dbSession, user, provider, source);
} finally {
dbClient.closeSession(dbSession);
}
}
- private UserDto registerNewUser(DbSession dbSession, UserIdentity user, IdentityProvider provider) {
+ private UserDto registerNewUser(DbSession dbSession, UserIdentity user, IdentityProvider provider, AuthenticationEvent.Source source) {
if (!provider.allowsUsersToSignUp()) {
- throw new UnauthorizedException(format("'%s' users are not allowed to sign up", provider.getKey()));
+ throw AuthenticationException.newBuilder()
+ .setSource(source)
+ .setLogin(user.getLogin())
+ .setMessage(format("'%s' users are not allowed to sign up", provider.getKey()))
+ .build();
}
String email = user.getEmail();
if (email != null && dbClient.userDao().doesEmailExist(dbSession, email)) {
- throw new UnauthorizedException(format(
- "You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.", email));
+ throw AuthenticationException.newBuilder()
+ .setLogin(user.getLogin())
+ .setSource(source)
+ .setMessage(format(
+ "You can't sign up because email '%s' is already used by an existing user. This means that you probably already registered with another account.",
+ email))
+ .build();
}
String userLogin = user.getLogin();
import org.sonar.api.server.ServerSide;
import org.sonar.db.DbClient;
import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.event.AuthenticationEvent;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
import static org.sonar.api.web.ServletFilter.UrlPattern;
import static org.sonar.api.web.ServletFilter.UrlPattern.Builder.staticResourcePatterns;
+import static org.sonar.server.authentication.AuthenticationError.handleAuthenticationError;
import static org.sonar.server.authentication.ws.LoginAction.AUTH_LOGIN_URL;
import static org.sonar.server.authentication.ws.ValidateAction.AUTH_VALIDATE_URL;
import static org.sonar.server.user.ServerUserSession.createForAnonymous;
private final BasicAuthenticator basicAuthenticator;
private final SsoAuthenticator ssoAuthenticator;
private final ThreadLocalUserSession threadLocalSession;
+ private final AuthenticationEvent authenticationEvent;
public UserSessionInitializer(DbClient dbClient, Settings settings, JwtHttpHandler jwtHttpHandler, BasicAuthenticator basicAuthenticator,
- SsoAuthenticator ssoAuthenticator, ThreadLocalUserSession threadLocalSession) {
+ SsoAuthenticator ssoAuthenticator, ThreadLocalUserSession threadLocalSession, AuthenticationEvent authenticationEvent) {
this.dbClient = dbClient;
this.settings = settings;
this.jwtHttpHandler = jwtHttpHandler;
this.basicAuthenticator = basicAuthenticator;
this.ssoAuthenticator = ssoAuthenticator;
this.threadLocalSession = threadLocalSession;
+ this.authenticationEvent = authenticationEvent;
}
public boolean initUserSession(HttpServletRequest request, HttpServletResponse response) {
}
setUserSession(request, response);
return true;
+ } catch (AuthenticationException e) {
+ authenticationEvent.failure(request, e);
+ if (isWsUrl(path)) {
+ response.setStatus(HTTP_UNAUTHORIZED);
+ return false;
+ }
+ handleAuthenticationError(e, response);
+ return false;
} catch (UnauthorizedException e) {
response.setStatus(HTTP_UNAUTHORIZED);
if (isWsUrl(path)) {
}
}
+ private static boolean shouldContinueFilterOnError(String path) {
+ if (isWsUrl(path)) {
+ return false;
+ }
+ // WS should stop here. Rails page should continue in order to deal with redirection
+ return true;
+ }
+
private void setUserSession(HttpServletRequest request, HttpServletResponse response) {
Optional<UserDto> user = authenticate(request, response);
if (user.isPresent()) {
*/
package org.sonar.server.authentication.event;
+import java.io.Serializable;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
+import org.sonar.api.server.authentication.BaseIdentityProvider;
+import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
void login(HttpServletRequest request, String login, Source source);
+ void failure(HttpServletRequest request, AuthenticationException e);
+
enum Method {
- BASIC, BASIC_TOKEN, FORM, FORM_TOKEN, SSO, OAUTH2, EXTERNAL
+ /**
+ * HTTP basic authentication with a login and password.
+ */
+ BASIC,
+ /**
+ * HTTP basic authentication with a security token.
+ */
+ BASIC_TOKEN,
+ /**
+ * SQ login form authentication with a login and password.
+ */
+ FORM,
+ /**
+ * SSO authentication (ie. with HTTP headers)
+ */
+ SSO,
+ /**
+ * OAUTH2 authentication.
+ */
+ OAUTH2,
+ /**
+ * JWT authentication (ie. with a session token).
+ */
+ JWT,
+ /**
+ * External authentication (ie. fully implemented out of SQ's core code, see {@link BaseIdentityProvider}).
+ */
+ EXTERNAL
}
enum Provider {
- LOCAL, SSO, REALM, EXTERNAL
+ /**
+ * User authentication made against data in SQ's User table.
+ */
+ LOCAL,
+ /**
+ * User authentication made by SSO provider.
+ */
+ SSO,
+ /**
+ * User authentication made by Realm based provider (eg. LDAP).
+ */
+ REALM,
+ /**
+ * User authentication made by JWT token information.
+ */
+ JWT,
+ /**
+ * User authentication made by external provider (see {@link BaseIdentityProvider}).
+ */
+ EXTERNAL
}
- class Source {
+ final class Source implements Serializable {
private static final String LOCAL_PROVIDER_NAME = "local";
private static final Source SSO_INSTANCE = new Source(Method.SSO, Provider.SSO, "sso");
+ private static final Source JWT_INSTANCE = new Source(Method.JWT, Provider.JWT, "jwt");
private final Method method;
private final Provider provider;
return new Source(method, Provider.LOCAL, LOCAL_PROVIDER_NAME);
}
- public static Source oauth2(String providerName) {
- return new Source(Method.OAUTH2, Provider.EXTERNAL, providerName);
+ public static Source oauth2(OAuth2IdentityProvider identityProvider) {
+ return new Source(
+ Method.OAUTH2, Provider.EXTERNAL,
+ requireNonNull(identityProvider, "identityProvider can't be null").getName());
}
public static Source realm(Method method, String providerName) {
return SSO_INSTANCE;
}
- public Method getMethod() {
+ public static Source jwt() {
+ return JWT_INSTANCE;
+ }
+
+ public static Source external(BaseIdentityProvider identityProvider) {
+ return new Source(
+ Method.EXTERNAL, Provider.EXTERNAL,
+ requireNonNull(identityProvider, "identityProvider can't be null").getName());
+ }
+
+ Method getMethod() {
return method;
}
- public Provider getProvider() {
+ Provider getProvider() {
return provider;
}
- public String getProviderName() {
+ String getProviderName() {
return providerName;
}
@Override
public void login(HttpServletRequest request, @Nullable String login, Source source) {
LOGGER.info("login success [method|{}][provider|{}|{}][IP|{}|{}][login|{}]",
- source.getMethod(), source.getProvider(), source.getProviderName(), request.getRemoteAddr(), getAllIps(request),
- login == null ? "" : login);
+ source.getMethod(), source.getProvider(), source.getProviderName(),
+ request.getRemoteAddr(), getAllIps(request),
+ emptyIfNull(login));
}
private static String getAllIps(HttpServletRequest request) {
return Collections.list(request.getHeaders("X-Forwarded-For")).stream().collect(Collectors.join(Joiner.on(",")));
}
+ @Override
+ public void failure(HttpServletRequest request, AuthenticationException e) {
+ Source source = e.getSource();
+ LOGGER.info("login failure [cause|{}][method|{}][provider|{}|{}][IP|{}|{}][login|{}]",
+ emptyIfNull(e.getMessage()),
+ source.getMethod(), source.getProvider(), source.getProviderName(),
+ request.getRemoteAddr(), getAllIps(request),
+ emptyIfNull(e.getLogin()));
+ }
+
+ private static String emptyIfNull(@Nullable String login) {
+ return login == null ? "" : 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.authentication.event;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Exception thrown in case of authentication failure.
+ * <p>
+ * This exception contains the source of authentication and, if present, the login on which the login attempt occurred.
+ * </p>
+ * <p>
+ * Given that {@link #source} and {@link #login} will be logged to file, be very careful <strong>not to set the login
+ * when the login is a security token</strong>.
+ * </p>
+ */
+public class AuthenticationException extends RuntimeException {
+ private final AuthenticationEvent.Source source;
+ @CheckForNull
+ private final String login;
+
+ private AuthenticationException(Builder builder) {
+ super(builder.message);
+ this.source = requireNonNull(builder.source, "source can't be null");
+ this.login = builder.login;
+ }
+
+ public AuthenticationEvent.Source getSource() {
+ return source;
+ }
+
+ @CheckForNull
+ public String getLogin() {
+ return login;
+ }
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ @CheckForNull
+ private AuthenticationEvent.Source source;
+ @CheckForNull
+ private String login;
+ @CheckForNull
+ private String message;
+
+ private Builder() {
+ // use static factory method
+ }
+
+ public Builder setSource(AuthenticationEvent.Source source) {
+ this.source = source;
+ return this;
+ }
+
+ public Builder setLogin(@Nullable String login) {
+ this.login = login;
+ return this;
+ }
+
+ public Builder setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public AuthenticationException build() {
+ return new AuthenticationException(this);
+ }
+ }
+}
package org.sonar.server.authentication.ws;
import java.io.IOException;
+import javax.annotation.Nullable;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.CredentialsAuthenticator;
import org.sonar.server.authentication.JwtHttpHandler;
+import org.sonar.server.authentication.event.AuthenticationEvent;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class LoginAction extends ServletFilter {
private final CredentialsAuthenticator credentialsAuthenticator;
private final JwtHttpHandler jwtHttpHandler;
private final ThreadLocalUserSession threadLocalUserSession;
+ private final AuthenticationEvent authenticationEvent;
- public LoginAction(DbClient dbClient, CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler, ThreadLocalUserSession threadLocalUserSession) {
+ public LoginAction(DbClient dbClient, CredentialsAuthenticator credentialsAuthenticator, JwtHttpHandler jwtHttpHandler,
+ ThreadLocalUserSession threadLocalUserSession, AuthenticationEvent authenticationEvent) {
this.dbClient = dbClient;
this.credentialsAuthenticator = credentialsAuthenticator;
this.jwtHttpHandler = jwtHttpHandler;
this.threadLocalUserSession = threadLocalUserSession;
+ this.authenticationEvent = authenticationEvent;
}
@Override
response.setStatus(HTTP_BAD_REQUEST);
return;
}
+
+ String login = request.getParameter("login");
+ String password = request.getParameter("password");
try {
- UserDto userDto = authenticate(request);
+ UserDto userDto = authenticate(request, login, password);
jwtHttpHandler.generateToken(userDto, request, response);
threadLocalUserSession.set(ServerUserSession.createForUser(dbClient, userDto));
// TODO add chain.doFilter when Rack filter will not be executed after this filter (or use a Servlet)
+ } catch (AuthenticationException e) {
+ authenticationEvent.failure(request, e);
+ response.setStatus(HTTP_UNAUTHORIZED);
} catch (UnauthorizedException e) {
response.setStatus(e.httpCode());
}
}
- private UserDto authenticate(HttpServletRequest request) {
- String login = request.getParameter("login");
- String password = request.getParameter("password");
+ private UserDto authenticate(HttpServletRequest request, @Nullable String login, @Nullable String password) {
if (isEmpty(login) || isEmpty(password)) {
- throw new UnauthorizedException();
+ throw AuthenticationException.newBuilder()
+ .setSource(Source.local(Method.FORM))
+ .setLogin(login)
+ .setMessage("empty login and/or password")
+ .build();
}
return credentialsAuthenticator.authenticate(login, password, request, Method.FORM);
}
package org.sonar.server.authentication.ws;
-import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
-
import java.io.IOException;
import java.util.Optional;
import javax.servlet.FilterChain;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.BasicAuthenticator;
import org.sonar.server.authentication.JwtHttpHandler;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonarqube.ws.MediaTypes;
+import static org.sonar.api.CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY;
+
public class ValidateAction extends ServletFilter {
public static final String AUTH_VALIDATE_URL = "/api/authentication/validate";
return true;
}
return !settings.getBoolean(CORE_FORCE_AUTHENTICATION_PROPERTY);
- } catch (UnauthorizedException e) {
+ } catch (AuthenticationException e) {
return false;
}
}
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSession;
public class BaseContextFactoryTest {
- static String PUBLIC_ROOT_URL = "https://mydomain.com";
+ private static final String PUBLIC_ROOT_URL = "https://mydomain.com";
- static UserIdentity USER_IDENTITY = UserIdentity.builder()
+ private static final UserIdentity USER_IDENTITY = UserIdentity.builder()
.setProviderLogin("johndoo")
.setLogin("id:johndoo")
.setName("John")
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
- DbClient dbClient = dbTester.getDbClient();
+ private DbClient dbClient = dbTester.getDbClient();
- DbSession dbSession = dbTester.getSession();
+ private DbSession dbSession = dbTester.getSession();
- ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
+ private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
- UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
- Server server = mock(Server.class);
+ private UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
+ private Server server = mock(Server.class);
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class);
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
+ private BaseIdentityProvider identityProvider = mock(BaseIdentityProvider.class);
+ private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
- BaseContextFactory underTest = new BaseContextFactory(dbClient, userIdentityAuthenticator, server, jwtHttpHandler, threadLocalUserSession);
+ private BaseContextFactory underTest = new BaseContextFactory(dbClient, userIdentityAuthenticator, server, jwtHttpHandler, threadLocalUserSession);
@Before
public void setUp() throws Exception {
UserDto userDto = dbClient.userDao().insert(dbSession, newUserDto());
dbSession.commit();
- when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider)).thenReturn(userDto);
+ when(identityProvider.getName()).thenReturn("provIdeur Nameuh");
+ when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider, AuthenticationEvent.Source.external(identityProvider))).thenReturn(userDto);
}
@Test
when(request.getSession()).thenReturn(session);
context.authenticate(USER_IDENTITY);
- verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
+ verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider, AuthenticationEvent.Source.external(identityProvider));
verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(request), eq(response));
verify(threadLocalUserSession).set(any(UserSession.class));
}
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTesting;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.usertoken.UserTokenAuthenticator;
import static com.google.common.base.Charsets.UTF_8;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC_TOKEN;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class BasicAuthenticatorTest {
public void fail_to_authenticate_when_no_login() throws Exception {
when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64(":" + PASSWORD));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC)).withoutLogin());
try {
underTest.authenticate(request);
} finally {
public void fail_to_authenticate_when_invalid_header() throws Exception {
when(request.getHeader("Authorization")).thenReturn("Basic Invàlid");
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC)).withoutLogin().andNoPublicMessage());
expectedException.expectMessage("Invalid basic header");
underTest.authenticate(request);
}
when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.empty());
when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC_TOKEN)).withoutLogin());
try {
underTest.authenticate(request);
} finally {
when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of("Unknown user"));
when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(Method.BASIC_TOKEN)).withoutLogin());
try {
underTest.authenticate(request);
} finally {
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC_TOKEN;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class CredentialsAuthenticatorTest {
.setSalt(SALT)
.setLocal(true));
- UserDto userDto = executeAuthenticate();
+ UserDto userDto = executeAuthenticate(BASIC);
assertThat(userDto.getLogin()).isEqualTo(LOGIN);
verify(authenticationEvent).login(request, LOGIN, Source.local(BASIC));
}
.setSalt("Wrong salt")
.setLocal(true));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC)).withLogin(LOGIN));
+ expectedException.expectMessage("wrong password");
try {
- executeAuthenticate();
+ executeAuthenticate(BASIC);
} finally {
verifyZeroInteractions(authenticationEvent);
}
.setLogin(LOGIN)
.setLocal(false));
- executeAuthenticate();
+ executeAuthenticate(BASIC);
verify(externalAuthenticator).authenticate(LOGIN, PASSWORD, request, BASIC);
verifyZeroInteractions(authenticationEvent);
@Test
public void fail_to_authenticate_authenticate_external_user_when_no_external_authentication() throws Exception {
- when(externalAuthenticator.authenticate(LOGIN, PASSWORD, request, BASIC)).thenReturn(Optional.empty());
+ when(externalAuthenticator.authenticate(LOGIN, PASSWORD, request, BASIC_TOKEN)).thenReturn(Optional.empty());
insertUser(newUserDto()
.setLogin(LOGIN)
.setLocal(false));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC_TOKEN)).withLogin(LOGIN));
+ expectedException.expectMessage("User is not local");
try {
- executeAuthenticate();
+ executeAuthenticate(BASIC_TOKEN);
} finally {
verifyZeroInteractions(authenticationEvent);
}
.setSalt(SALT)
.setLocal(true));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC)).withLogin(LOGIN));
+ expectedException.expectMessage("null password in DB");
try {
- executeAuthenticate();
+ executeAuthenticate(BASIC);
} finally {
verifyZeroInteractions(authenticationEvent);
}
.setSalt(null)
.setLocal(true));
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.local(BASIC_TOKEN)).withLogin(LOGIN));
+ expectedException.expectMessage("null salt");
try {
- executeAuthenticate();
+ executeAuthenticate(BASIC_TOKEN);
} finally {
verifyZeroInteractions(authenticationEvent);
}
}
- private UserDto executeAuthenticate() {
- return underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
+ private UserDto executeAuthenticate(AuthenticationEvent.Method method) {
+ return underTest.authenticate(LOGIN, PASSWORD, request, method);
}
private UserDto insertUser(UserDto userDto) {
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
-import org.sonar.api.platform.Server;
-import org.sonar.server.exceptions.UnauthorizedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class JwtCsrfVerifierTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
- static final int TIMEOUT = 30;
- static final String CSRF_STATE = "STATE";
- static final String JAVA_WS_URL = "/api/metrics/create";
+ private static final int TIMEOUT = 30;
+ private static final String CSRF_STATE = "STATE";
+ private static final String JAVA_WS_URL = "/api/metrics/create";
+ private static final String LOGIN = "foo login";
- ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
+ private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
- Server server = mock(Server.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- HttpServletRequest request = mock(HttpServletRequest.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
- JwtCsrfVerifier underTest = new JwtCsrfVerifier();
+ private JwtCsrfVerifier underTest = new JwtCsrfVerifier();
@Before
public void setUp() throws Exception {
mockRequestCsrf(CSRF_STATE);
mockPostJavaWsRequest();
- underTest.verifyState(request, CSRF_STATE);
+ underTest.verifyState(request, CSRF_STATE, LOGIN);
}
@Test
- public void fail_with_unauthorized_when_state_header_is_not_the_same_as_state_parameter() throws Exception {
+ public void fail_with_AuthenticationException_when_state_header_is_not_the_same_as_state_parameter() throws Exception {
mockRequestCsrf("other value");
mockPostJavaWsRequest();
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, CSRF_STATE);
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("wrong CSFR in request");
+ underTest.verifyState(request, CSRF_STATE, LOGIN);
}
@Test
- public void fail_with_unauthorized_when_state_is_null() throws Exception {
+ public void fail_with_AuthenticationException_when_state_is_null() throws Exception {
mockRequestCsrf(CSRF_STATE);
mockPostJavaWsRequest();
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, null);
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("missing reference CSRF value");
+ underTest.verifyState(request, null, LOGIN);
}
@Test
- public void fail_with_unauthorized_when_state_parameter_is_empty() throws Exception {
+ public void fail_with_AuthenticationException_when_state_parameter_is_empty() throws Exception {
mockRequestCsrf(CSRF_STATE);
mockPostJavaWsRequest();
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, "");
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("missing reference CSRF value");
+ underTest.verifyState(request, "", LOGIN);
}
@Test
when(request.getRequestURI()).thenReturn(JAVA_WS_URL);
when(request.getMethod()).thenReturn("POST");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, CSRF_STATE);
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("wrong CSFR in request");
+ underTest.verifyState(request, CSRF_STATE, LOGIN);
}
@Test
when(request.getRequestURI()).thenReturn(JAVA_WS_URL);
when(request.getMethod()).thenReturn("PUT");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, CSRF_STATE);
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("wrong CSFR in request");
+ underTest.verifyState(request, CSRF_STATE, LOGIN);
}
@Test
when(request.getRequestURI()).thenReturn(JAVA_WS_URL);
when(request.getMethod()).thenReturn("DELETE");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, CSRF_STATE);
+ thrown.expect(authenticationException().from(Source.local(Method.JWT)).withLogin(LOGIN));
+ thrown.expectMessage("wrong CSFR in request");
+ underTest.verifyState(request, CSRF_STATE, LOGIN);
}
@Test
when(request.getRequestURI()).thenReturn(JAVA_WS_URL);
when(request.getMethod()).thenReturn("GET");
- underTest.verifyState(request, null);
+ underTest.verifyState(request, null, LOGIN);
}
@Test
when(request.getRequestURI()).thenReturn(uri);
when(request.getMethod()).thenReturn(method);
- underTest.verifyState(request, null);
+ underTest.verifyState(request, null, LOGIN);
}
}
underTest.validateToken(request, response);
- verify(jwtCsrfVerifier).verifyState(request, CSRF_STATE);
+ verify(jwtCsrfVerifier).verifyState(request, CSRF_STATE, USER_LOGIN);
}
@Test
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryImpl;
-import org.sonar.server.exceptions.UnauthorizedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.authentication.JwtSerializer.JwtSession;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class JwtSerializerTest {
+ private static final String A_SECRET_KEY = "HrPSavOYLNNrwTY+SOqpChr7OwvbR/zbDLdVXRN0+Eg=";
+ private static final String USER_LOGIN = "john";
+
@Rule
public ExpectedException expectedException = ExpectedException.none();
- static final String A_SECRET_KEY = "HrPSavOYLNNrwTY+SOqpChr7OwvbR/zbDLdVXRN0+Eg=";
-
- static final String USER_LOGIN = "john";
-
private Settings settings = new MapSettings();
private System2 system2 = System2.INSTANCE;
private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE;
.signWith(SignatureAlgorithm.HS256, decodeSecretKey(A_SECRET_KEY))
.compact();
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.jwt()).withLogin(USER_LOGIN));
expectedException.expectMessage("Token id hasn't been found");
underTest.decode(token);
}
.signWith(SignatureAlgorithm.HS256, decodeSecretKey(A_SECRET_KEY))
.compact();
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.jwt()).withoutLogin());
expectedException.expectMessage("Token subject hasn't been found");
underTest.decode(token);
}
.signWith(SignatureAlgorithm.HS256, decodeSecretKey(A_SECRET_KEY))
.compact();
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.jwt()).withLogin(USER_LOGIN));
expectedException.expectMessage("Token expiration date hasn't been found");
underTest.decode(token);
}
.signWith(SignatureAlgorithm.HS256, decodeSecretKey(A_SECRET_KEY))
.compact();
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.jwt()).withLogin(USER_LOGIN));
expectedException.expectMessage("Token creation date hasn't been found");
underTest.decode(token);
}
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
assertThat(oAuth2IdentityProvider.isCallbackCalled()).isTrue();
if (expectLoginLog) {
- verify(authenticationEvent).login(request, LOGIN, Source.oauth2(oAuth2IdentityProvider.getName()));
+ verify(authenticationEvent).login(request, LOGIN, Source.oauth2(oAuth2IdentityProvider));
} else {
verifyZeroInteractions(authenticationEvent);
}
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
public class OAuth2ContextFactoryTest {
+ private static final String PROVIDER_KEY = "github";
+ private static final String SECURED_PUBLIC_ROOT_URL = "https://mydomain.com";
+ private static final String NOT_SECURED_PUBLIC_URL = "http://mydomain.com";
+ private static final String PROVIDER_NAME = "provider name";
+ private static final UserIdentity USER_IDENTITY = UserIdentity.builder()
+ .setProviderLogin("johndoo")
+ .setLogin("id:johndoo")
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
@Rule
public ExpectedException thrown = ExpectedException.none();
- static String PROVIDER_KEY = "github";
-
- static String SECURED_PUBLIC_ROOT_URL = "https://mydomain.com";
- static String NOT_SECURED_PUBLIC_URL = "http://mydomain.com";
-
- static UserIdentity USER_IDENTITY = UserIdentity.builder()
- .setProviderLogin("johndoo")
- .setLogin("id:johndoo")
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
- DbClient dbClient = dbTester.getDbClient();
-
- DbSession dbSession = dbTester.getSession();
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
- ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
- UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
- Server server = mock(Server.class);
- OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+ private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
+ private UserIdentityAuthenticator userIdentityAuthenticator = mock(UserIdentityAuthenticator.class);
+ private Server server = mock(Server.class);
+ private OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
+ private 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);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
+ private HttpSession session = mock(HttpSession.class);
+ private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class);
- OAuth2ContextFactory underTest = new OAuth2ContextFactory(dbClient, threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler);
+ private OAuth2ContextFactory underTest = new OAuth2ContextFactory(dbClient, threadLocalUserSession, userIdentityAuthenticator, server, csrfVerifier, jwtHttpHandler);
@Before
public void setUp() throws Exception {
when(request.getSession()).thenReturn(session);
when(identityProvider.getKey()).thenReturn(PROVIDER_KEY);
- when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider)).thenReturn(userDto);
+ when(identityProvider.getName()).thenReturn(PROVIDER_NAME);
+ when(userIdentityAuthenticator.authenticate(USER_IDENTITY, identityProvider, Source.oauth2(identityProvider))).thenReturn(userDto);
}
@Test
callback.authenticate(USER_IDENTITY);
- verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider);
+ verify(userIdentityAuthenticator).authenticate(USER_IDENTITY, identityProvider, Source.oauth2(identityProvider));
verify(jwtHttpHandler).generateToken(any(UserDto.class), eq(request), eq(response));
verify(threadLocalUserSession).set(any(UserSession.class));
}
callback.verifyCsrfState();
- verify(csrfVerifier).verifyState(request, response);
+ verify(csrfVerifier).verifyState(request, response, identityProvider);
}
private OAuth2IdentityProvider.InitContext newInitContext() {
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.sonar.api.platform.Server;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.api.server.authentication.OAuth2IdentityProvider;
+import org.sonar.server.authentication.event.AuthenticationEvent;
import static org.apache.commons.codec.digest.DigestUtils.sha1Hex;
import static org.apache.commons.codec.digest.DigestUtils.sha256Hex;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class OAuthCsrfVerifierTest {
+ private static final String PROVIDER_NAME = "provider name";
@Rule
public ExpectedException thrown = ExpectedException.none();
- ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
+ private ArgumentCaptor<Cookie> cookieArgumentCaptor = ArgumentCaptor.forClass(Cookie.class);
- Server server = mock(Server.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
- HttpServletRequest request = mock(HttpServletRequest.class);
+ private OAuth2IdentityProvider identityProvider = mock(OAuth2IdentityProvider.class);
+ private Server server = mock(Server.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
- OAuthCsrfVerifier underTest = new OAuthCsrfVerifier();
+ private OAuthCsrfVerifier underTest = new OAuthCsrfVerifier();
@Before
public void setUp() throws Exception {
when(server.getContextPath()).thenReturn("");
+ when(identityProvider.getName()).thenReturn(PROVIDER_NAME);
}
@Test
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha256Hex(state))});
when(request.getParameter("state")).thenReturn(state);
- underTest.verifyState(request, response);
+ underTest.verifyState(request, response, identityProvider);
verify(response).addCookie(cookieArgumentCaptor.capture());
Cookie updatedCookie = cookieArgumentCaptor.getValue();
}
@Test
- public void fail_with_unauthorized_when_state_cookie_is_not_the_same_as_state_parameter() throws Exception {
+ public void fail_with_AuthenticationException_when_state_cookie_is_not_the_same_as_state_parameter() throws Exception {
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha1Hex("state"))});
when(request.getParameter("state")).thenReturn("other value");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, response);
+ thrown.expect(authenticationException().from(AuthenticationEvent.Source.oauth2(identityProvider)).withoutLogin());
+ thrown.expectMessage("CSRF state value is invalid");
+ underTest.verifyState(request, response, identityProvider);
}
@Test
- public void fail_to_verify_state_when_state_cookie_is_null() throws Exception {
+ public void fail_with_AuthenticationException_when_state_cookie_is_null() throws Exception {
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", null)});
when(request.getParameter("state")).thenReturn("state");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, response);
+ thrown.expect(authenticationException().from(AuthenticationEvent.Source.oauth2(identityProvider)).withoutLogin());
+ thrown.expectMessage("CSRF state value is invalid");
+ underTest.verifyState(request, response, identityProvider);
}
@Test
- public void fail_with_unauthorized_when_state_parameter_is_empty() throws Exception {
+ public void fail_with_AuthenticationException_when_state_parameter_is_empty() throws Exception {
when(request.getCookies()).thenReturn(new Cookie[] {new Cookie("OAUTHSTATE", sha1Hex("state"))});
when(request.getParameter("state")).thenReturn("");
- thrown.expect(UnauthorizedException.class);
- underTest.verifyState(request, response);
+ thrown.expect(authenticationException().from(AuthenticationEvent.Source.oauth2(identityProvider)).withoutLogin());
+ thrown.expectMessage("CSRF state value is invalid");
+ underTest.verifyState(request, response, identityProvider);
+ }
+
+ @Test
+ public void fail_with_AuthenticationException_when_cookie_is_missing() throws Exception {
+ when(request.getCookies()).thenReturn(new Cookie[] {});
+
+ thrown.expect(authenticationException().from(AuthenticationEvent.Source.oauth2(identityProvider)).withoutLogin());
+ thrown.expectMessage("Cookie 'OAUTHSTATE' is missing");
+ underTest.verifyState(request, response, identityProvider);
}
private void verifyCookie(Cookie cookie) {
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.SecurityRealmFactory;
import static java.util.Arrays.asList;
import static org.mockito.Mockito.when;
import static org.sonar.db.user.UserTesting.newUserDto;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC_TOKEN;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class RealmAuthenticatorTest {
private ArgumentCaptor<UserIdentity> userIdentityArgumentCaptor = ArgumentCaptor.forClass(UserIdentity.class);
private ArgumentCaptor<IdentityProvider> identityProviderArgumentCaptor = ArgumentCaptor.forClass(IdentityProvider.class);
+ private ArgumentCaptor<AuthenticationEvent.Source> sourceCaptor = ArgumentCaptor.forClass(Source.class);
private Settings settings = new MapSettings();
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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.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();
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@Test
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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
assertThat(identityProviderArgumentCaptor.getValue().getKey()).isEqualTo("sonarqube");
assertThat(identityProviderArgumentCaptor.getValue().getName()).isEqualTo("sonarqube");
assertThat(identityProviderArgumentCaptor.getValue().getDisplay()).isNull();
assertThat(identityProviderArgumentCaptor.getValue().isEnabled()).isTrue();
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@Test
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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
assertThat(identityProviderArgumentCaptor.getValue().getName()).isEqualTo("sonarqube");
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
executeStartWithGroupSync();
executeAuthenticate();
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
assertThat(userIdentity.shouldSyncGroups()).isTrue();
assertThat(userIdentity.getGroups()).containsOnly("group1", "group2");
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@Test
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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
assertThat(userIdentityArgumentCaptor.getValue().getName()).isEqualTo(LOGIN);
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
executeStartWithoutGroupSync();
executeAuthenticate();
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
assertThat(identityProviderArgumentCaptor.getValue().allowsUsersToSignUp()).isTrue();
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
executeStartWithoutGroupSync();
executeAuthenticate();
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
assertThat(identityProviderArgumentCaptor.getValue().allowsUsersToSignUp()).isFalse();
- verify(authenticationEvent).login(request, LOGIN, AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, LOGIN, Source.realm(BASIC, REALM_NAME));
}
@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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
executeStartWithoutGroupSync();
executeAuthenticate("LOGIN");
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
assertThat(userIdentity.getLogin()).isEqualTo("login");
assertThat(userIdentity.getProviderLogin()).isEqualTo("login");
- verify(authenticationEvent).login(request, "login", AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, "login", Source.realm(BASIC, REALM_NAME));
}
@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);
+ when(userIdentityAuthenticator.authenticate(any(UserIdentity.class), any(IdentityProvider.class), any(Source.class))).thenReturn(USER);
executeStartWithoutGroupSync();
executeAuthenticate("LoGiN");
- verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture());
+ verify(userIdentityAuthenticator).authenticate(userIdentityArgumentCaptor.capture(), identityProviderArgumentCaptor.capture(), sourceCaptor.capture());
UserIdentity userIdentity = userIdentityArgumentCaptor.getValue();
assertThat(userIdentity.getLogin()).isEqualTo("LoGiN");
assertThat(userIdentity.getProviderLogin()).isEqualTo("LoGiN");
- verify(authenticationEvent).login(request, "LoGiN", AuthenticationEvent.Source.realm(BASIC, REALM_NAME));
+ verify(authenticationEvent).login(request, "LoGiN", Source.realm(BASIC, REALM_NAME));
}
@Test
when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN));
+ expectedException.expectMessage("No user details");
try {
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
} finally {
when(authenticator.doAuthenticate(any(Authenticator.Context.class))).thenReturn(false);
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.realm(BASIC, REALM_NAME)).withLogin(LOGIN));
+ expectedException.expectMessage("realm returned authenticate=false");
try {
underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
} finally {
@Test
public void fail_to_authenticate_when_any_exception_is_thrown() throws Exception {
executeStartWithoutGroupSync();
- doThrow(IllegalArgumentException.class).when(authenticator).doAuthenticate(any(Authenticator.Context.class));
+ String expectedMessage = "emulating exception in doAuthenticate";
+ doThrow(new IllegalArgumentException(expectedMessage)).when(authenticator).doAuthenticate(any(Authenticator.Context.class));
- when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(null);
+ when(externalUsersProvider.doGetUserDetails(any(ExternalUsersProvider.Context.class))).thenReturn(new UserDetails());
- expectedException.expect(UnauthorizedException.class);
+ expectedException.expect(authenticationException().from(Source.realm(BASIC_TOKEN, REALM_NAME)).withLogin(LOGIN));
+ expectedException.expectMessage(expectedMessage);
try {
- underTest.authenticate(LOGIN, PASSWORD, request, BASIC);
+ underTest.authenticate(LOGIN, PASSWORD, request, BASIC_TOKEN);
} finally {
verifyZeroInteractions(authenticationEvent);
}
import org.junit.rules.ExpectedException;
import org.sonar.api.config.MapSettings;
import org.sonar.api.config.Settings;
-import org.sonar.api.server.authentication.UnauthorizedException;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
import static org.mockito.Mockito.mock;
import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
public class UserIdentityAuthenticatorTest {
private static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
.setKey("github")
+ .setName("name of github")
.setEnabled(true)
.setAllowsUsersToSignUp(true);
@Test
public void authenticate_new_user() throws Exception {
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.realm(Method.BASIC, IDENTITY_PROVIDER.getName()));
UserDto user = db.users().selectUserByLogin(USER_LOGIN).get();
assertThat(user).isNotNull();
.setExternalIdentity("old identity")
.setExternalIdentityProvider("old provide"));
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC));
UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
assertThat(userDto.isActive()).isTrue();
.setExternalIdentity("old identity")
.setExternalIdentityProvider("old provide"));
- underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, Source.local(Method.BASIC_TOKEN));
UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
assertThat(userDto.isActive()).isTrue();
.setLogin(user.getLogin())
.setName(user.getName())
.setGroups(newHashSet(groupName))
- .build(), IDENTITY_PROVIDER);
+ .build(), IDENTITY_PROVIDER, Source.sso());
assertThat(db.users().selectGroupIdsOfUser(user)).containsOnly(groupInDefaultOrg.getId());
}
.setName("Github")
.setEnabled(true)
.setAllowsUsersToSignUp(false);
+ Source source = Source.realm(Method.FORM, identityProvider.getName());
- thrown.expect(UnauthorizedException.class);
+ thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()));
thrown.expectMessage("'github' users are not allowed to sign up");
- underTest.authenticate(USER_IDENTITY, identityProvider);
+ underTest.authenticate(USER_IDENTITY, identityProvider, source);
}
@Test
.setLogin("Existing user with same email")
.setActive(true)
.setEmail("john@email.com"));
+ Source source = Source.realm(Method.FORM, IDENTITY_PROVIDER.getName());
- thrown.expect(UnauthorizedException.class);
+ thrown.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getLogin()));
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);
+ underTest.authenticate(USER_IDENTITY, IDENTITY_PROVIDER, source);
}
private void authenticate(String login, String... groups) {
.setName("John")
// No group
.setGroups(Arrays.stream(groups).collect(Collectors.toSet()))
- .build(), IDENTITY_PROVIDER);
+ .build(), IDENTITY_PROVIDER, Source.sso());
}
}
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.ServerUserSession;
import org.sonar.server.user.ThreadLocalUserSession;
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
- DbClient dbClient = dbTester.getDbClient();
+ private DbClient dbClient = dbTester.getDbClient();
- DbSession dbSession = dbTester.getSession();
+ private DbSession dbSession = dbTester.getSession();
- ThreadLocalUserSession userSession = mock(ThreadLocalUserSession.class);
+ private ThreadLocalUserSession userSession = mock(ThreadLocalUserSession.class);
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpServletResponse response = mock(HttpServletResponse.class);
+ private HttpServletRequest request = mock(HttpServletRequest.class);
+ private HttpServletResponse response = mock(HttpServletResponse.class);
- JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
- BasicAuthenticator basicAuthenticator = mock(BasicAuthenticator.class);
- SsoAuthenticator ssoAuthenticator = mock(SsoAuthenticator.class);
+ private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+ private BasicAuthenticator basicAuthenticator = mock(BasicAuthenticator.class);
+ private SsoAuthenticator ssoAuthenticator = mock(SsoAuthenticator.class);
- Settings settings = new MapSettings();
+ private Settings settings = new MapSettings();
- UserDto user = newUserDto();
+ private UserDto user = newUserDto();
- UserSessionInitializer underTest = new UserSessionInitializer(dbClient, settings, jwtHttpHandler, basicAuthenticator, ssoAuthenticator, userSession);
+ private UserSessionInitializer underTest = new UserSessionInitializer(dbClient, settings, jwtHttpHandler, basicAuthenticator, ssoAuthenticator, userSession, mock(AuthenticationEvent.class));
@Before
public void setUp() throws Exception {
import static org.mockito.Mockito.when;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import static org.sonar.server.authentication.event.AuthenticationException.newBuilder;
public class AuthenticationEventImplTest {
@Rule
verifyLog("login success [method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4][login|foo]");
}
+ @Test
+ public void failure_fails_with_NPE_if_request_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ underTest.failure(null, newBuilder().setSource(Source.sso()).build());
+ }
+
+ @Test
+ public void failure_fails_with_NPE_if_AuthenticationException_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ underTest.failure(mock(HttpServletRequest.class), null);
+ }
+
+ @Test
+ public void failure_creates_INFO_log_with_empty_login_if_AuthenticationException_has_no_login() {
+ AuthenticationException exception = newBuilder().setSource(Source.sso()).setMessage("message").build();
+ underTest.failure(mockRequest(), exception);
+
+ verifyLog("login failure [cause|message][method|SSO][provider|SSO|sso][IP||][login|]");
+ }
+
+ @Test
+ public void failure_creates_INFO_log_with_empty_cause_if_AuthenticationException_has_no_message() {
+ AuthenticationException exception = newBuilder().setSource(Source.sso()).setLogin("FoO").build();
+ underTest.failure(mockRequest(), exception);
+
+ verifyLog("login failure [cause|][method|SSO][provider|SSO|sso][IP||][login|FoO]");
+ }
+
+ @Test
+ public void failure_creates_INFO_log_with_method_provider_and_login() {
+ AuthenticationException exception = newBuilder()
+ .setSource(Source.realm(Method.BASIC, "some provider name"))
+ .setMessage("something got terribly wrong")
+ .setLogin("BaR")
+ .build();
+ underTest.failure(mockRequest(), exception);
+
+ verifyLog("login failure [cause|something got terribly wrong][method|BASIC][provider|REALM|some provider name][IP||][login|BaR]");
+ }
+
+ @Test
+ public void failure_logs_remote_ip_from_request() {
+ AuthenticationException exception = newBuilder()
+ .setSource(Source.realm(Method.EXTERNAL, "bar"))
+ .setMessage("Damn it!")
+ .setLogin("Baaad")
+ .build();
+ underTest.failure(mockRequest("1.2.3.4"), exception);
+
+ verifyLog("login failure [cause|Damn it!][method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|][login|Baaad]");
+ }
+
+ @Test
+ public void failure_logs_X_Forwarded_For_header_from_request() {
+ AuthenticationException exception = newBuilder()
+ .setSource(Source.realm(Method.EXTERNAL, "bar"))
+ .setMessage("Hop la!")
+ .setLogin("foo")
+ .build();
+ HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5"));
+ underTest.failure(request, exception);
+
+ verifyLog("login failure [cause|Hop la!][method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5][login|foo]");
+ }
+
+ @Test
+ public void failure_logs_X_Forwarded_For_header_from_request_and_supports_multiple_headers() {
+ AuthenticationException exception = newBuilder()
+ .setSource(Source.realm(Method.EXTERNAL, "bar"))
+ .setMessage("Boom!")
+ .setLogin("foo")
+ .build();
+ HttpServletRequest request = mockRequest("1.2.3.4", asList("2.3.4.5", "6.5.4.3"), asList("9.5.6.7"), asList("6.3.2.4"));
+ underTest.failure(request, exception);
+
+ verifyLog("login failure [cause|Boom!][method|EXTERNAL][provider|REALM|bar][IP|1.2.3.4|2.3.4.5,6.5.4.3,9.5.6.7,6.3.2.4][login|foo]");
+ }
+
private void verifyLog(String expected) {
assertThat(logTester.logs()).hasSize(1);
assertThat(logTester.logs(LoggerLevel.INFO))
*/
package org.sonar.server.authentication.event;
+import java.io.Serializable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.server.authentication.BaseIdentityProvider;
+import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
import static org.sonar.server.authentication.event.AuthenticationEvent.Provider;
import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
}
@Test
- public void oauth2_fails_with_NPE_if_providerName_is_null() {
+ public void oauth2_fails_with_NPE_if_provider_is_null() {
expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("provider name can't be null");
+ expectedException.expectMessage("identityProvider can't be null");
Source.oauth2(null);
}
@Test
- public void oauth2_creates_source_instance_with_specified_provider_name_and_hardcoded_provider_and_method() {
- Source underTest = Source.oauth2("some name");
+ public void oauth2_fails_with_NPE_if_providerName_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("provider name can't be null");
- assertThat(underTest.getMethod()).isEqualTo(Method.OAUTH2);
- assertThat(underTest.getProvider()).isEqualTo(Provider.EXTERNAL);
- assertThat(underTest.getProviderName()).isEqualTo("some name");
+ Source.oauth2(newOauth2IdentityProvider(null));
}
@Test
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("provider name can't be empty");
- Source.oauth2("");
+ Source.oauth2(newOauth2IdentityProvider(""));
+ }
+
+ @Test
+ public void oauth2_creates_source_instance_with_specified_provider_name_and_hardcoded_provider_and_method() {
+ Source underTest = Source.oauth2(newOauth2IdentityProvider("some name"));
+
+ assertThat(underTest.getMethod()).isEqualTo(Method.OAUTH2);
+ assertThat(underTest.getProvider()).isEqualTo(Provider.EXTERNAL);
+ assertThat(underTest.getProviderName()).isEqualTo("some name");
}
@Test
assertThat(underTest).isSameAs(Source.sso());
}
+
+ @Test
+ public void jwt_returns_source_instance_with_hardcoded_method_provider_and_providerName() {
+ Source underTest = Source.jwt();
+
+ assertThat(underTest.getMethod()).isEqualTo(Method.JWT);
+ assertThat(underTest.getProvider()).isEqualTo(Provider.JWT);
+ assertThat(underTest.getProviderName()).isEqualTo("jwt");
+
+ assertThat(underTest).isSameAs(Source.jwt());
+ }
+
+ @Test
+ public void external_fails_with_NPE_if_provider_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("identityProvider can't be null");
+
+ Source.external(null);
+ }
+
+ @Test
+ public void external_fails_with_NPE_if_providerName_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("provider name can't be null");
+
+ Source.external(newBasicIdentityProvider(null));
+ }
+
+ @Test
+ public void external_fails_with_IAE_if_providerName_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("provider name can't be empty");
+
+ Source.external(newBasicIdentityProvider(""));
+ }
+
+ @Test
+ public void external_creates_source_instance_with_specified_provider_name_and_hardcoded_provider_and_method() {
+ Source underTest = Source.external(newBasicIdentityProvider("some name"));
+
+ assertThat(underTest.getMethod()).isEqualTo(Method.EXTERNAL);
+ assertThat(underTest.getProvider()).isEqualTo(Provider.EXTERNAL);
+ assertThat(underTest.getProviderName()).isEqualTo("some name");
+ }
+
+ @Test
+ public void source_is_serializable() {
+ assertThat(Serializable.class.isAssignableFrom(Source.class)).isTrue();
+ }
+
+ @Test
+ public void toString_displays_all_fields() {
+ assertThat(Source.sso().toString())
+ .isEqualTo("Source{method=SSO, provider=SSO, providerName='sso'}");
+ assertThat(Source.oauth2(newOauth2IdentityProvider("bou")).toString())
+ .isEqualTo("Source{method=OAUTH2, provider=EXTERNAL, providerName='bou'}");
+ }
+
+ @Test
+ public void source_implements_equals_on_all_fields() {
+ assertThat(Source.sso()).isEqualTo(Source.sso());
+ assertThat(Source.sso()).isNotEqualTo(Source.jwt());
+ assertThat(Source.jwt()).isEqualTo(Source.jwt());
+ assertThat(Source.local(Method.BASIC)).isEqualTo(Source.local(Method.BASIC));
+ assertThat(Source.local(Method.BASIC)).isNotEqualTo(Source.local(Method.BASIC_TOKEN));
+ assertThat(Source.local(Method.BASIC)).isNotEqualTo(Source.sso());
+ assertThat(Source.local(Method.BASIC)).isNotEqualTo(Source.jwt());
+ assertThat(Source.local(Method.BASIC)).isNotEqualTo(Source.oauth2(newOauth2IdentityProvider("voo")));
+ assertThat(Source.oauth2(newOauth2IdentityProvider("foo")))
+ .isEqualTo(Source.oauth2(newOauth2IdentityProvider("foo")));
+ assertThat(Source.oauth2(newOauth2IdentityProvider("foo")))
+ .isNotEqualTo(Source.oauth2(newOauth2IdentityProvider("bar")));
+ }
+
+ private static OAuth2IdentityProvider newOauth2IdentityProvider(String name) {
+ OAuth2IdentityProvider mock = mock(OAuth2IdentityProvider.class);
+ when(mock.getName()).thenReturn(name);
+ return mock;
+ }
+
+ private static BaseIdentityProvider newBasicIdentityProvider(String name) {
+ BaseIdentityProvider mock = mock(BaseIdentityProvider.class);
+ when(mock.getName()).thenReturn(name);
+ return mock;
+ }
}
--- /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.event;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Source;
+
+/**
+ * Matcher for {@link AuthenticationException} to be used with {@link org.junit.rules.ExpectedException} JUnit Rule.
+ *
+ * <p>
+ * Usage:
+ * <pre>
+ * expectedException.expect(authenticationException().from(Source.local(Method.BASIC_TOKEN)).withoutLogin());
+ * </pre>
+ * </p>
+ */
+public class AuthenticationExceptionMatcher extends TypeSafeMatcher<Throwable> {
+ private final Source source;
+ @CheckForNull
+ private final String login;
+
+ private AuthenticationExceptionMatcher(Source source, @Nullable String login) {
+ this.source = requireNonNull(source, "source can't be null");
+ this.login = login;
+ }
+
+ public static Builder authenticationException() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private Source source;
+
+ public Builder from(Source source) {
+ this.source = checkSource(source);
+ return this;
+ }
+
+ public AuthenticationExceptionMatcher withLogin(String login) {
+ requireNonNull(login, "expected login can't be null");
+ return new AuthenticationExceptionMatcher(checkSource(source), login);
+ }
+
+ public AuthenticationExceptionMatcher withoutLogin() {
+ return new AuthenticationExceptionMatcher(checkSource(source), null);
+ }
+
+ private static Source checkSource(Source source) {
+ return requireNonNull(source, "expected source can't be null");
+ }
+ }
+
+ @Override
+ protected boolean matchesSafely(Throwable throwable) {
+ return check(throwable) == null;
+ }
+
+ private String check(Throwable throwable) {
+ if (!throwable.getClass().isAssignableFrom(AuthenticationException.class)) {
+ return "exception is not a AuthenticationException";
+ }
+ AuthenticationException authenticationException = (AuthenticationException) throwable;
+ if (!this.source.equals(authenticationException.getSource())) {
+ return "source is \"" + authenticationException.getSource() + "\"";
+ }
+
+ String login = authenticationException.getLogin();
+ if (this.login == null) {
+ if (login != null) {
+ return "login is \"" + login + "\"";
+ }
+ } else if (login == null) {
+ return "login is null";
+ } else if (!this.login.equals(login)) {
+ return "login is \"" + login + "\"";
+ }
+
+ return null;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("AuthenticationException with source ").appendText(source.toString());
+ if (login == null) {
+ description.appendText(" and no login");
+ } else {
+ description.appendText(" and login \"").appendText(login).appendText("\"");
+ }
+ }
+
+ @Override
+ protected void describeMismatchSafely(Throwable item, Description mismatchDescription) {
+ mismatchDescription.appendText(check(item));
+ }
+}
--- /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.event;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.authentication.event.AuthenticationEvent.Source;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AuthenticationExceptionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void build_fails_with_NPE_if_source_is_null() {
+ AuthenticationException.Builder builder = AuthenticationException.newBuilder()
+ .setLogin("login")
+ .setMessage("message");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("source can't be null");
+
+ builder.build();
+ }
+
+ @Test
+ public void build_does_not_fail_if_login_is_null() {
+ AuthenticationException exception = AuthenticationException.newBuilder()
+ .setSource(Source.sso())
+ .setMessage("message")
+ .build();
+
+ assertThat(exception.getSource()).isEqualTo(Source.sso());
+ assertThat(exception.getMessage()).isEqualTo("message");
+ assertThat(exception.getLogin()).isNull();
+ }
+
+ @Test
+ public void build_does_not_fail_if_message_is_null() {
+ AuthenticationException exception = AuthenticationException.newBuilder()
+ .setSource(Source.sso())
+ .setLogin("login")
+ .build();
+
+ assertThat(exception.getSource()).isEqualTo(Source.sso());
+ assertThat(exception.getMessage()).isNull();
+ assertThat(exception.getLogin()).isEqualTo("login");
+ }
+
+ @Test
+ public void builder_set_methods_do_not_fail_if_login_is_null() {
+ AuthenticationException.newBuilder().setSource(null).setLogin(null).setMessage(null);
+ }
+
+}
import org.sonar.db.user.UserTesting;
import org.sonar.server.authentication.CredentialsAuthenticator;
import org.sonar.server.authentication.JwtHttpHandler;
+import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.user.ThreadLocalUserSession;
private UserDto user = UserTesting.newUserDto().setLogin(LOGIN);
- private LoginAction underTest = new LoginAction(dbClient, credentialsAuthenticator, jwtHttpHandler, threadLocalUserSession);
+ private LoginAction underTest = new LoginAction(dbClient, credentialsAuthenticator, jwtHttpHandler, threadLocalUserSession, mock(AuthenticationEvent.class));
@Before
public void setUp() throws Exception {
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.config.Settings;
import org.sonar.api.config.MapSettings;
+import org.sonar.api.config.Settings;
import org.sonar.server.authentication.BasicAuthenticator;
import org.sonar.server.authentication.JwtHttpHandler;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.test.JsonAssert;
import org.sonarqube.ws.MediaTypes;
Settings settings = new MapSettings();
- ValidateAction underTest = new ValidateAction(settings, basicAuthenticator, jwtHttpHandler);
+ ValidateAction underTest = new ValidateAction(settings, basicAuthenticator, jwtHttpHandler);
@Before
public void setUp() throws Exception {
@Test
public void return_false_when_jwt_throws_unauthorized_exception() throws Exception {
- doThrow(UnauthorizedException.class).when(jwtHttpHandler).validateToken(request, response);
+ doThrow(AuthenticationException.class).when(jwtHttpHandler).validateToken(request, response);
when(basicAuthenticator.authenticate(request)).thenReturn(Optional.empty());
underTest.doFilter(request, response, chain);
@Test
public void return_false_when_basic_authenticator_throws_unauthorized_exception() throws Exception {
when(jwtHttpHandler.validateToken(request, response)).thenReturn(Optional.empty());
- doThrow(UnauthorizedException.class).when(basicAuthenticator).authenticate(request);
+ doThrow(AuthenticationException.class).when(basicAuthenticator).authenticate(request);
underTest.doFilter(request, response, chain);