to UserRegistrar to help understanding its responsibility.
IdentityProviderRepository.class,
BaseContextFactory.class,
OAuth2ContextFactory.class,
- UserIdentityAuthenticatorImpl.class,
+ UserRegistrarImpl.class,
OAuthCsrfVerifier.class,
UserSessionInitializer.class,
JwtSerializer.class,
import org.sonar.api.server.authentication.BaseIdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSessionFactory;
public class BaseContextFactory {
private final ThreadLocalUserSession threadLocalUserSession;
- private final UserIdentityAuthenticator userIdentityAuthenticator;
+ private final UserRegistrar userRegistrar;
private final Server server;
private final JwtHttpHandler jwtHttpHandler;
private final UserSessionFactory userSessionFactory;
- public BaseContextFactory(UserIdentityAuthenticator userIdentityAuthenticator, Server server, JwtHttpHandler jwtHttpHandler,
- ThreadLocalUserSession threadLocalUserSession, UserSessionFactory userSessionFactory) {
+ public BaseContextFactory(UserRegistrar userRegistrar, Server server, JwtHttpHandler jwtHttpHandler,
+ ThreadLocalUserSession threadLocalUserSession, UserSessionFactory userSessionFactory) {
this.userSessionFactory = userSessionFactory;
- this.userIdentityAuthenticator = userIdentityAuthenticator;
+ this.userRegistrar = userRegistrar;
this.server = server;
this.jwtHttpHandler = jwtHttpHandler;
this.threadLocalUserSession = threadLocalUserSession;
@Override
public void authenticate(UserIdentity userIdentity) {
- UserDto userDto = userIdentityAuthenticator.authenticate(
- UserIdentityAuthenticatorParameters.builder()
+ UserDto userDto = userRegistrar.register(
+ UserRegistration.builder()
.setUserIdentity(userIdentity)
.setProvider(identityProvider)
.setSource(Source.external(identityProvider))
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.authentication.event.AuthenticationException;
private final Configuration config;
private final SecurityRealmFactory securityRealmFactory;
- private final UserIdentityAuthenticator userIdentityAuthenticator;
+ private final UserRegistrar userRegistrar;
private final AuthenticationEvent authenticationEvent;
private SecurityRealm realm;
private ExternalGroupsProvider externalGroupsProvider;
public CredentialsExternalAuthentication(Configuration config, SecurityRealmFactory securityRealmFactory,
- UserIdentityAuthenticator userIdentityAuthenticator, AuthenticationEvent authenticationEvent) {
+ UserRegistrar userRegistrar, AuthenticationEvent authenticationEvent) {
this.config = config;
this.securityRealmFactory = securityRealmFactory;
- this.userIdentityAuthenticator = userIdentityAuthenticator;
+ this.userRegistrar = userRegistrar;
this.authenticationEvent = authenticationEvent;
}
Collection<String> groups = externalGroupsProvider.doGetGroups(context);
userIdentityBuilder.setGroups(new HashSet<>(groups));
}
- return userIdentityAuthenticator.authenticate(
- UserIdentityAuthenticatorParameters.builder()
+ return userRegistrar.register(
+ UserRegistration.builder()
.setUserIdentity(userIdentityBuilder.build())
.setProvider(new ExternalIdentityProvider())
.setSource(realmEventSource(method))
import org.sonar.api.utils.log.Loggers;
import org.sonar.db.user.UserDto;
import org.sonar.process.ProcessProperties;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.authentication.event.AuthenticationException;
private static final String LAST_REFRESH_TIME_TOKEN_PARAM = "ssoLastRefreshTime";
- private static final EnumSet<ProcessProperties.Property> SETTINGS = EnumSet.of(
+ private static final EnumSet<ProcessProperties.Property> PROPERTIES = EnumSet.of(
SONAR_WEB_SSO_LOGIN_HEADER,
SONAR_WEB_SSO_NAME_HEADER,
SONAR_WEB_SSO_EMAIL_HEADER,
private final System2 system2;
private final Configuration config;
- private final UserIdentityAuthenticator userIdentityAuthenticator;
+ private final UserRegistrar userRegistrar;
private final JwtHttpHandler jwtHttpHandler;
private final AuthenticationEvent authenticationEvent;
+ private final Map<String, String> settingsByKey = new HashMap<>();
private boolean enabled = false;
- private Map<String, String> settingsByKey = new HashMap<>();
- public HttpHeadersAuthentication(System2 system2, Configuration config, UserIdentityAuthenticator userIdentityAuthenticator,
+ public HttpHeadersAuthentication(System2 system2, Configuration config, UserRegistrar userRegistrar,
JwtHttpHandler jwtHttpHandler, AuthenticationEvent authenticationEvent) {
this.system2 = system2;
this.config = config;
- this.userIdentityAuthenticator = userIdentityAuthenticator;
+ this.userRegistrar = userRegistrar;
this.jwtHttpHandler = jwtHttpHandler;
this.authenticationEvent = authenticationEvent;
}
if (config.getBoolean(SONAR_WEB_SSO_ENABLE.getKey()).orElse(false)) {
LOG.info("HTTP headers authentication enabled");
enabled = true;
- SETTINGS.forEach(entry -> settingsByKey.put(entry.getKey(), config.get(entry.getKey()).orElse(entry.getDefaultValue())));
+ PROPERTIES.forEach(entry -> settingsByKey.put(entry.getKey(), config.get(entry.getKey()).orElse(entry.getDefaultValue())));
}
}
String groupsValue = getHeaderValue(headerValuesByNames, SONAR_WEB_SSO_GROUPS_HEADER.getKey());
userIdentityBuilder.setGroups(groupsValue == null ? Collections.emptySet() : new HashSet<>(COMA_SPLITTER.splitToList(groupsValue)));
}
- return userIdentityAuthenticator.authenticate(
- UserIdentityAuthenticatorParameters.builder()
+ return userRegistrar.register(
+ UserRegistration.builder()
.setUserIdentity(userIdentityBuilder.build())
.setProvider(new SsoIdentityProvider())
.setSource(Source.sso())
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSessionFactory;
public class OAuth2ContextFactory {
private final ThreadLocalUserSession threadLocalUserSession;
- private final UserIdentityAuthenticator userIdentityAuthenticator;
+ private final UserRegistrar userRegistrar;
private final Server server;
private final OAuthCsrfVerifier csrfVerifier;
private final JwtHttpHandler jwtHttpHandler;
private final UserSessionFactory userSessionFactory;
private final OAuth2AuthenticationParameters oAuthParameters;
- public OAuth2ContextFactory(ThreadLocalUserSession threadLocalUserSession, UserIdentityAuthenticator userIdentityAuthenticator, Server server,
- OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler, UserSessionFactory userSessionFactory, OAuth2AuthenticationParameters oAuthParameters) {
+ public OAuth2ContextFactory(ThreadLocalUserSession threadLocalUserSession, UserRegistrar userRegistrar, Server server,
+ OAuthCsrfVerifier csrfVerifier, JwtHttpHandler jwtHttpHandler, UserSessionFactory userSessionFactory, OAuth2AuthenticationParameters oAuthParameters) {
this.threadLocalUserSession = threadLocalUserSession;
- this.userIdentityAuthenticator = userIdentityAuthenticator;
+ this.userRegistrar = userRegistrar;
this.server = server;
this.csrfVerifier = csrfVerifier;
this.jwtHttpHandler = jwtHttpHandler;
public void authenticate(UserIdentity userIdentity) {
Boolean allowEmailShift = oAuthParameters.getAllowEmailShift(request).orElse(false);
Boolean allowUpdateLogin = oAuthParameters.getAllowUpdateLogin(request).orElse(false);
- UserDto userDto = userIdentityAuthenticator.authenticate(
- UserIdentityAuthenticatorParameters.builder()
+ UserDto userDto = userRegistrar.register(
+ UserRegistration.builder()
.setUserIdentity(userIdentity)
.setProvider(identityProvider)
.setSource(AuthenticationEvent.Source.oauth2(identityProvider))
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.authentication;
-
-import org.sonar.db.user.UserDto;
-
-public interface UserIdentityAuthenticator {
-
- UserDto authenticate(UserIdentityAuthenticatorParameters authenticatorParameters);
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.authentication;
-
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.server.authentication.IdentityProvider;
-import org.sonar.api.server.authentication.UserIdentity;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.UserDto;
-import org.sonar.db.user.UserGroupDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.event.AuthenticationException;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
-import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
-import org.sonar.server.organization.DefaultOrganization;
-import org.sonar.server.organization.DefaultOrganizationProvider;
-import org.sonar.server.organization.OrganizationFlags;
-import org.sonar.server.organization.OrganizationUpdater;
-import org.sonar.server.user.ExternalIdentity;
-import org.sonar.server.user.NewUser;
-import org.sonar.server.user.UpdateUser;
-import org.sonar.server.user.UserUpdater;
-import org.sonar.server.usergroups.DefaultGroupFinder;
-
-import static com.google.common.base.Preconditions.checkState;
-import static java.lang.String.format;
-import static java.util.Collections.singletonList;
-import static java.util.Objects.requireNonNull;
-import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-import static org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
-
-public class UserIdentityAuthenticatorImpl implements UserIdentityAuthenticator {
-
- private static final Logger LOGGER = Loggers.get(UserIdentityAuthenticatorImpl.class);
-
- private final DbClient dbClient;
- private final UserUpdater userUpdater;
- private final DefaultOrganizationProvider defaultOrganizationProvider;
- private final OrganizationFlags organizationFlags;
- private final OrganizationUpdater organizationUpdater;
- private final DefaultGroupFinder defaultGroupFinder;
-
- public UserIdentityAuthenticatorImpl(DbClient dbClient, UserUpdater userUpdater, DefaultOrganizationProvider defaultOrganizationProvider, OrganizationFlags organizationFlags,
- OrganizationUpdater organizationUpdater, DefaultGroupFinder defaultGroupFinder) {
- this.dbClient = dbClient;
- this.userUpdater = userUpdater;
- this.defaultOrganizationProvider = defaultOrganizationProvider;
- this.organizationFlags = organizationFlags;
- this.organizationUpdater = organizationUpdater;
- this.defaultGroupFinder = defaultGroupFinder;
- }
-
- @Override
- public UserDto authenticate(UserIdentityAuthenticatorParameters authenticatorParameters) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- UserDto userDto = getUser(dbSession, authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider());
- if (userDto == null) {
- return registerNewUser(dbSession, null, authenticatorParameters);
- }
- if (!userDto.isActive()) {
- return registerNewUser(dbSession, userDto, authenticatorParameters);
- }
- return registerExistingUser(dbSession, userDto, authenticatorParameters);
- }
- }
-
- @CheckForNull
- private UserDto getUser(DbSession dbSession, UserIdentity userIdentity, IdentityProvider provider) {
- UserDto user = dbClient.userDao().selectByExternalIdAndIdentityProvider(dbSession, getProviderIdOrProviderLogin(userIdentity), provider.getKey());
- if (user != null) {
- return user;
- }
- // We need to search by login because :
- // 1. user may have been provisioned,
- // 2. user may have been disabled.
- String login = userIdentity.getLogin();
- if (login == null) {
- return null;
- }
- return dbClient.userDao().selectByLogin(dbSession, login);
- }
-
- private UserDto registerNewUser(DbSession dbSession, @Nullable UserDto disabledUser, UserIdentityAuthenticatorParameters authenticatorParameters) {
- Optional<UserDto> otherUserToIndex = detectEmailUpdate(dbSession, authenticatorParameters);
- NewUser newUser = createNewUser(authenticatorParameters);
- if (disabledUser == null) {
- return userUpdater.createAndCommit(dbSession, newUser, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
- }
- return userUpdater.reactivateAndCommit(dbSession, disabledUser, newUser, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
- }
-
- private UserDto registerExistingUser(DbSession dbSession, UserDto userDto, UserIdentityAuthenticatorParameters authenticatorParameters) {
- UpdateUser update = new UpdateUser()
- .setEmail(authenticatorParameters.getUserIdentity().getEmail())
- .setName(authenticatorParameters.getUserIdentity().getName())
- .setExternalIdentity(new ExternalIdentity(
- authenticatorParameters.getProvider().getKey(),
- authenticatorParameters.getUserIdentity().getProviderLogin(),
- authenticatorParameters.getUserIdentity().getProviderId()));
- String login = authenticatorParameters.getUserIdentity().getLogin();
- if (login != null) {
- update.setLogin(login);
- }
- detectLoginUpdate(dbSession, userDto, update, authenticatorParameters);
- Optional<UserDto> otherUserToIndex = detectEmailUpdate(dbSession, authenticatorParameters);
- userUpdater.updateAndCommit(dbSession, userDto, update, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
- return userDto;
- }
-
- private Optional<UserDto> detectEmailUpdate(DbSession dbSession, UserIdentityAuthenticatorParameters authenticatorParameters) {
- String email = authenticatorParameters.getUserIdentity().getEmail();
- if (email == null) {
- return Optional.empty();
- }
- List<UserDto> existingUsers = dbClient.userDao().selectByEmail(dbSession, email);
- if (existingUsers.isEmpty()) {
- return Optional.empty();
- }
- if (existingUsers.size() > 1) {
- throw generateExistingEmailError(authenticatorParameters, email);
- }
-
- UserDto existingUser = existingUsers.get(0);
- if (existingUser == null
- || Objects.equals(existingUser.getLogin(), authenticatorParameters.getUserIdentity().getLogin())
- || (Objects.equals(existingUser.getExternalId(), getProviderIdOrProviderLogin(authenticatorParameters.getUserIdentity()))
- && Objects.equals(existingUser.getExternalIdentityProvider(), authenticatorParameters.getProvider().getKey()))) {
- return Optional.empty();
- }
- ExistingEmailStrategy existingEmailStrategy = authenticatorParameters.getExistingEmailStrategy();
- switch (existingEmailStrategy) {
- case ALLOW:
- existingUser.setEmail(null);
- dbClient.userDao().update(dbSession, existingUser);
- return Optional.of(existingUser);
- case WARN:
- throw new EmailAlreadyExistsRedirectionException(email, existingUser, authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider());
- case FORBID:
- throw generateExistingEmailError(authenticatorParameters, email);
- default:
- throw new IllegalStateException(format("Unknown strategy %s", existingEmailStrategy));
- }
- }
-
- private void detectLoginUpdate(DbSession dbSession, UserDto user, UpdateUser update, UserIdentityAuthenticatorParameters authenticatorParameters) {
- String newLogin = update.login();
- if (!update.isLoginChanged() || user.getLogin().equals(newLogin)) {
- return;
- }
- if (!organizationFlags.isEnabled(dbSession)) {
- return;
- }
- String personalOrganizationUuid = user.getOrganizationUuid();
- if (personalOrganizationUuid == null) {
- return;
- }
- Optional<OrganizationDto> personalOrganization = dbClient.organizationDao().selectByUuid(dbSession, personalOrganizationUuid);
- checkState(personalOrganization.isPresent(),
- "Cannot find personal organization uuid '%s' for user '%s'", personalOrganizationUuid, user.getLogin());
- UpdateLoginStrategy updateLoginStrategy = authenticatorParameters.getUpdateLoginStrategy();
- switch (updateLoginStrategy) {
- case ALLOW:
- organizationUpdater.updateOrganizationKey(dbSession, personalOrganization.get(), requireNonNull(newLogin, "new login cannot be null"));
- return;
- case WARN:
- throw new UpdateLoginRedirectionException(authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider(), user, personalOrganization.get());
- default:
- throw new IllegalStateException(format("Unknown strategy %s", updateLoginStrategy));
- }
- }
-
- private void syncGroups(DbSession dbSession, UserIdentity userIdentity, UserDto userDto) {
- if (!userIdentity.shouldSyncGroups()) {
- return;
- }
- String userLogin = userDto.getLogin();
- Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(userLogin)).get(userLogin));
- Set<String> identityGroups = userIdentity.getGroups();
- LOGGER.debug("List of groups returned by the identity provider '{}'", identityGroups);
-
- Collection<String> groupsToAdd = Sets.difference(identityGroups, userGroups);
- Collection<String> groupsToRemove = Sets.difference(userGroups, identityGroups);
- Collection<String> allGroups = new ArrayList<>(groupsToAdd);
- allGroups.addAll(groupsToRemove);
- DefaultOrganization defaultOrganization = defaultOrganizationProvider.get();
- Map<String, GroupDto> groupsByName = dbClient.groupDao().selectByNames(dbSession, defaultOrganization.getUuid(), allGroups)
- .stream()
- .collect(uniqueIndex(GroupDto::getName));
-
- addGroups(dbSession, userDto, groupsToAdd, groupsByName);
- removeGroups(dbSession, userDto, groupsToRemove, groupsByName);
- }
-
- private void addGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToAdd, Map<String, GroupDto> groupsByName) {
- groupsToAdd.stream().map(groupsByName::get).filter(Objects::nonNull).forEach(
- groupDto -> {
- LOGGER.debug("Adding group '{}' to user '{}'", groupDto.getName(), userDto.getLogin());
- dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
- });
- }
-
- private void removeGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToRemove, Map<String, GroupDto> groupsByName) {
- Optional<GroupDto> defaultGroup = getDefaultGroup(dbSession);
- groupsToRemove.stream().map(groupsByName::get)
- .filter(Objects::nonNull)
- // user should be member of default group only when organizations are disabled, as the IdentityProvider API doesn't handle yet
- // organizations
- .filter(group -> !defaultGroup.isPresent() || !group.getId().equals(defaultGroup.get().getId()))
- .forEach(groupDto -> {
- LOGGER.debug("Removing group '{}' from user '{}'", groupDto.getName(), userDto.getLogin());
- dbClient.userGroupDao().delete(dbSession, groupDto.getId(), userDto.getId());
- });
- }
-
- private Optional<GroupDto> getDefaultGroup(DbSession dbSession) {
- return organizationFlags.isEnabled(dbSession) ? Optional.empty() : Optional.of(defaultGroupFinder.findDefaultGroup(dbSession, defaultOrganizationProvider.get().getUuid()));
- }
-
- private static NewUser createNewUser(UserIdentityAuthenticatorParameters authenticatorParameters) {
- String identityProviderKey = authenticatorParameters.getProvider().getKey();
- if (!authenticatorParameters.getProvider().allowsUsersToSignUp()) {
- throw AuthenticationException.newBuilder()
- .setSource(authenticatorParameters.getSource())
- .setLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
- .setMessage(format("User signup disabled for provider '%s'", identityProviderKey))
- .setPublicMessage(format("'%s' users are not allowed to sign up", identityProviderKey))
- .build();
- }
- return NewUser.builder()
- .setLogin(authenticatorParameters.getUserIdentity().getLogin())
- .setEmail(authenticatorParameters.getUserIdentity().getEmail())
- .setName(authenticatorParameters.getUserIdentity().getName())
- .setExternalIdentity(
- new ExternalIdentity(
- identityProviderKey,
- authenticatorParameters.getUserIdentity().getProviderLogin(),
- authenticatorParameters.getUserIdentity().getProviderId()))
- .build();
- }
-
- private static UserDto[] toArray(Optional<UserDto> userDto) {
- return userDto.map(u -> new UserDto[] {u}).orElse(new UserDto[] {});
- }
-
- private static AuthenticationException generateExistingEmailError(UserIdentityAuthenticatorParameters authenticatorParameters, String email) {
- return AuthenticationException.newBuilder()
- .setSource(authenticatorParameters.getSource())
- .setLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
- .setMessage(format("Email '%s' is already used", email))
- .setPublicMessage(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();
- }
-
- private static String getProviderIdOrProviderLogin(UserIdentity userIdentity) {
- String providerId = userIdentity.getProviderId();
- return providerId == null ? userIdentity.getProviderLogin() : providerId;
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.authentication;
-
-import org.sonar.api.server.authentication.IdentityProvider;
-import org.sonar.api.server.authentication.UserIdentity;
-import org.sonar.server.authentication.event.AuthenticationEvent;
-
-import static java.util.Objects.requireNonNull;
-
-class UserIdentityAuthenticatorParameters {
-
- /**
- * Strategy to be executed when the email of the user is already used by another user
- */
- enum ExistingEmailStrategy {
- /**
- * Authentication is allowed, the email is moved from other user to current user
- */
- ALLOW,
- /**
- * Authentication process is stopped, the user is redirected to a page explaining that the email is already used
- */
- WARN,
- /**
- * Forbid authentication of the user
- */
- FORBID
- }
-
- /**
- * Strategy to be executed when the login of the user is updated
- */
- enum UpdateLoginStrategy {
- /**
- * Authentication is allowed, the login of the user updated
- */
- ALLOW,
- /**
- * Authentication process is stopped, the user is redirected to a page explaining that the login will be updated.
- * It only happens when personal organizations are activated
- */
- WARN
- }
-
- private final UserIdentity userIdentity;
- private final IdentityProvider provider;
- private final AuthenticationEvent.Source source;
- private final ExistingEmailStrategy existingEmailStrategy;
- private final UpdateLoginStrategy updateLoginStrategy;
-
- UserIdentityAuthenticatorParameters(Builder builder) {
- this.userIdentity = builder.userIdentity;
- this.provider = builder.provider;
- this.source = builder.source;
- this.existingEmailStrategy = builder.existingEmailStrategy;
- this.updateLoginStrategy = builder.updateLoginStrategy;
- }
-
- public UserIdentity getUserIdentity() {
- return userIdentity;
- }
-
- public IdentityProvider getProvider() {
- return provider;
- }
-
- public AuthenticationEvent.Source getSource() {
- return source;
- }
-
- public ExistingEmailStrategy getExistingEmailStrategy() {
- return existingEmailStrategy;
- }
-
- public UpdateLoginStrategy getUpdateLoginStrategy() {
- return updateLoginStrategy;
- }
-
- static UserIdentityAuthenticatorParameters.Builder builder() {
- return new Builder();
- }
-
- public static class Builder {
- private UserIdentity userIdentity;
- private IdentityProvider provider;
- private AuthenticationEvent.Source source;
- private ExistingEmailStrategy existingEmailStrategy;
- private UpdateLoginStrategy updateLoginStrategy;
-
- public Builder setUserIdentity(UserIdentity userIdentity) {
- this.userIdentity = userIdentity;
- return this;
- }
-
- public Builder setProvider(IdentityProvider provider) {
- this.provider = provider;
- return this;
- }
-
- public Builder setSource(AuthenticationEvent.Source source) {
- this.source = source;
- return this;
- }
-
- /**
- * Strategy to be executed when the email of the user is already used by another user
- */
- public Builder setExistingEmailStrategy(ExistingEmailStrategy existingEmailStrategy) {
- this.existingEmailStrategy = existingEmailStrategy;
- return this;
- }
-
- /**
- * Strategy to be executed when the login of the user has changed
- */
- public Builder setUpdateLoginStrategy(UpdateLoginStrategy updateLoginStrategy) {
- this.updateLoginStrategy = updateLoginStrategy;
- return this;
- }
-
- public UserIdentityAuthenticatorParameters build() {
- requireNonNull(userIdentity, "userIdentity must be set");
- requireNonNull(provider, "identityProvider must be set");
- requireNonNull(source, "Source must be set");
- requireNonNull(existingEmailStrategy, "existingEmailStrategy must be set ");
- requireNonNull(updateLoginStrategy, "updateLoginStrategy must be set");
- return new UserIdentityAuthenticatorParameters(this);
- }
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.authentication;
+
+import org.sonar.db.user.UserDto;
+
+public interface UserRegistrar {
+
+ UserDto register(UserRegistration registration);
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.authentication;
+
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserGroupDto;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.event.AuthenticationException;
+import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
+import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
+import org.sonar.server.organization.DefaultOrganization;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.organization.OrganizationFlags;
+import org.sonar.server.organization.OrganizationUpdater;
+import org.sonar.server.user.ExternalIdentity;
+import org.sonar.server.user.NewUser;
+import org.sonar.server.user.UpdateUser;
+import org.sonar.server.user.UserUpdater;
+import org.sonar.server.usergroups.DefaultGroupFinder;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static java.util.Collections.singletonList;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
+
+public class UserRegistrarImpl implements UserRegistrar {
+
+ private static final Logger LOGGER = Loggers.get(UserRegistrarImpl.class);
+
+ private final DbClient dbClient;
+ private final UserUpdater userUpdater;
+ private final DefaultOrganizationProvider defaultOrganizationProvider;
+ private final OrganizationFlags organizationFlags;
+ private final OrganizationUpdater organizationUpdater;
+ private final DefaultGroupFinder defaultGroupFinder;
+
+ public UserRegistrarImpl(DbClient dbClient, UserUpdater userUpdater, DefaultOrganizationProvider defaultOrganizationProvider, OrganizationFlags organizationFlags,
+ OrganizationUpdater organizationUpdater, DefaultGroupFinder defaultGroupFinder) {
+ this.dbClient = dbClient;
+ this.userUpdater = userUpdater;
+ this.defaultOrganizationProvider = defaultOrganizationProvider;
+ this.organizationFlags = organizationFlags;
+ this.organizationUpdater = organizationUpdater;
+ this.defaultGroupFinder = defaultGroupFinder;
+ }
+
+ @Override
+ public UserDto register(UserRegistration registration) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ UserDto userDto = getUser(dbSession, registration.getUserIdentity(), registration.getProvider());
+ if (userDto == null) {
+ return registerNewUser(dbSession, null, registration);
+ }
+ if (!userDto.isActive()) {
+ return registerNewUser(dbSession, userDto, registration);
+ }
+ return registerExistingUser(dbSession, userDto, registration);
+ }
+ }
+
+ @CheckForNull
+ private UserDto getUser(DbSession dbSession, UserIdentity userIdentity, IdentityProvider provider) {
+ UserDto user = dbClient.userDao().selectByExternalIdAndIdentityProvider(dbSession, getProviderIdOrProviderLogin(userIdentity), provider.getKey());
+ if (user != null) {
+ return user;
+ }
+ // We need to search by login because :
+ // 1. user may have been provisioned,
+ // 2. user may have been disabled.
+ String login = userIdentity.getLogin();
+ if (login == null) {
+ return null;
+ }
+ return dbClient.userDao().selectByLogin(dbSession, login);
+ }
+
+ private UserDto registerNewUser(DbSession dbSession, @Nullable UserDto disabledUser, UserRegistration authenticatorParameters) {
+ Optional<UserDto> otherUserToIndex = detectEmailUpdate(dbSession, authenticatorParameters);
+ NewUser newUser = createNewUser(authenticatorParameters);
+ if (disabledUser == null) {
+ return userUpdater.createAndCommit(dbSession, newUser, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
+ }
+ return userUpdater.reactivateAndCommit(dbSession, disabledUser, newUser, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
+ }
+
+ private UserDto registerExistingUser(DbSession dbSession, UserDto userDto, UserRegistration authenticatorParameters) {
+ UpdateUser update = new UpdateUser()
+ .setEmail(authenticatorParameters.getUserIdentity().getEmail())
+ .setName(authenticatorParameters.getUserIdentity().getName())
+ .setExternalIdentity(new ExternalIdentity(
+ authenticatorParameters.getProvider().getKey(),
+ authenticatorParameters.getUserIdentity().getProviderLogin(),
+ authenticatorParameters.getUserIdentity().getProviderId()));
+ String login = authenticatorParameters.getUserIdentity().getLogin();
+ if (login != null) {
+ update.setLogin(login);
+ }
+ detectLoginUpdate(dbSession, userDto, update, authenticatorParameters);
+ Optional<UserDto> otherUserToIndex = detectEmailUpdate(dbSession, authenticatorParameters);
+ userUpdater.updateAndCommit(dbSession, userDto, update, u -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), u), toArray(otherUserToIndex));
+ return userDto;
+ }
+
+ private Optional<UserDto> detectEmailUpdate(DbSession dbSession, UserRegistration authenticatorParameters) {
+ String email = authenticatorParameters.getUserIdentity().getEmail();
+ if (email == null) {
+ return Optional.empty();
+ }
+ List<UserDto> existingUsers = dbClient.userDao().selectByEmail(dbSession, email);
+ if (existingUsers.isEmpty()) {
+ return Optional.empty();
+ }
+ if (existingUsers.size() > 1) {
+ throw generateExistingEmailError(authenticatorParameters, email);
+ }
+
+ UserDto existingUser = existingUsers.get(0);
+ if (existingUser == null
+ || Objects.equals(existingUser.getLogin(), authenticatorParameters.getUserIdentity().getLogin())
+ || (Objects.equals(existingUser.getExternalId(), getProviderIdOrProviderLogin(authenticatorParameters.getUserIdentity()))
+ && Objects.equals(existingUser.getExternalIdentityProvider(), authenticatorParameters.getProvider().getKey()))) {
+ return Optional.empty();
+ }
+ ExistingEmailStrategy existingEmailStrategy = authenticatorParameters.getExistingEmailStrategy();
+ switch (existingEmailStrategy) {
+ case ALLOW:
+ existingUser.setEmail(null);
+ dbClient.userDao().update(dbSession, existingUser);
+ return Optional.of(existingUser);
+ case WARN:
+ throw new EmailAlreadyExistsRedirectionException(email, existingUser, authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider());
+ case FORBID:
+ throw generateExistingEmailError(authenticatorParameters, email);
+ default:
+ throw new IllegalStateException(format("Unknown strategy %s", existingEmailStrategy));
+ }
+ }
+
+ private void detectLoginUpdate(DbSession dbSession, UserDto user, UpdateUser update, UserRegistration authenticatorParameters) {
+ String newLogin = update.login();
+ if (!update.isLoginChanged() || user.getLogin().equals(newLogin)) {
+ return;
+ }
+ if (!organizationFlags.isEnabled(dbSession)) {
+ return;
+ }
+ String personalOrganizationUuid = user.getOrganizationUuid();
+ if (personalOrganizationUuid == null) {
+ return;
+ }
+ Optional<OrganizationDto> personalOrganization = dbClient.organizationDao().selectByUuid(dbSession, personalOrganizationUuid);
+ checkState(personalOrganization.isPresent(),
+ "Cannot find personal organization uuid '%s' for user '%s'", personalOrganizationUuid, user.getLogin());
+ UpdateLoginStrategy updateLoginStrategy = authenticatorParameters.getUpdateLoginStrategy();
+ switch (updateLoginStrategy) {
+ case ALLOW:
+ organizationUpdater.updateOrganizationKey(dbSession, personalOrganization.get(), requireNonNull(newLogin, "new login cannot be null"));
+ return;
+ case WARN:
+ throw new UpdateLoginRedirectionException(authenticatorParameters.getUserIdentity(), authenticatorParameters.getProvider(), user, personalOrganization.get());
+ default:
+ throw new IllegalStateException(format("Unknown strategy %s", updateLoginStrategy));
+ }
+ }
+
+ private void syncGroups(DbSession dbSession, UserIdentity userIdentity, UserDto userDto) {
+ if (!userIdentity.shouldSyncGroups()) {
+ return;
+ }
+ String userLogin = userDto.getLogin();
+ Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(userLogin)).get(userLogin));
+ Set<String> identityGroups = userIdentity.getGroups();
+ LOGGER.debug("List of groups returned by the identity provider '{}'", identityGroups);
+
+ Collection<String> groupsToAdd = Sets.difference(identityGroups, userGroups);
+ Collection<String> groupsToRemove = Sets.difference(userGroups, identityGroups);
+ Collection<String> allGroups = new ArrayList<>(groupsToAdd);
+ allGroups.addAll(groupsToRemove);
+ DefaultOrganization defaultOrganization = defaultOrganizationProvider.get();
+ Map<String, GroupDto> groupsByName = dbClient.groupDao().selectByNames(dbSession, defaultOrganization.getUuid(), allGroups)
+ .stream()
+ .collect(uniqueIndex(GroupDto::getName));
+
+ addGroups(dbSession, userDto, groupsToAdd, groupsByName);
+ removeGroups(dbSession, userDto, groupsToRemove, groupsByName);
+ }
+
+ private void addGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToAdd, Map<String, GroupDto> groupsByName) {
+ groupsToAdd.stream().map(groupsByName::get).filter(Objects::nonNull).forEach(
+ groupDto -> {
+ LOGGER.debug("Adding group '{}' to user '{}'", groupDto.getName(), userDto.getLogin());
+ dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setGroupId(groupDto.getId()).setUserId(userDto.getId()));
+ });
+ }
+
+ private void removeGroups(DbSession dbSession, UserDto userDto, Collection<String> groupsToRemove, Map<String, GroupDto> groupsByName) {
+ Optional<GroupDto> defaultGroup = getDefaultGroup(dbSession);
+ groupsToRemove.stream().map(groupsByName::get)
+ .filter(Objects::nonNull)
+ // user should be member of default group only when organizations are disabled, as the IdentityProvider API doesn't handle yet
+ // organizations
+ .filter(group -> !defaultGroup.isPresent() || !group.getId().equals(defaultGroup.get().getId()))
+ .forEach(groupDto -> {
+ LOGGER.debug("Removing group '{}' from user '{}'", groupDto.getName(), userDto.getLogin());
+ dbClient.userGroupDao().delete(dbSession, groupDto.getId(), userDto.getId());
+ });
+ }
+
+ private Optional<GroupDto> getDefaultGroup(DbSession dbSession) {
+ return organizationFlags.isEnabled(dbSession) ? Optional.empty() : Optional.of(defaultGroupFinder.findDefaultGroup(dbSession, defaultOrganizationProvider.get().getUuid()));
+ }
+
+ private static NewUser createNewUser(UserRegistration authenticatorParameters) {
+ String identityProviderKey = authenticatorParameters.getProvider().getKey();
+ if (!authenticatorParameters.getProvider().allowsUsersToSignUp()) {
+ throw AuthenticationException.newBuilder()
+ .setSource(authenticatorParameters.getSource())
+ .setLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
+ .setMessage(format("User signup disabled for provider '%s'", identityProviderKey))
+ .setPublicMessage(format("'%s' users are not allowed to sign up", identityProviderKey))
+ .build();
+ }
+ return NewUser.builder()
+ .setLogin(authenticatorParameters.getUserIdentity().getLogin())
+ .setEmail(authenticatorParameters.getUserIdentity().getEmail())
+ .setName(authenticatorParameters.getUserIdentity().getName())
+ .setExternalIdentity(
+ new ExternalIdentity(
+ identityProviderKey,
+ authenticatorParameters.getUserIdentity().getProviderLogin(),
+ authenticatorParameters.getUserIdentity().getProviderId()))
+ .build();
+ }
+
+ private static UserDto[] toArray(Optional<UserDto> userDto) {
+ return userDto.map(u -> new UserDto[] {u}).orElse(new UserDto[] {});
+ }
+
+ private static AuthenticationException generateExistingEmailError(UserRegistration authenticatorParameters, String email) {
+ return AuthenticationException.newBuilder()
+ .setSource(authenticatorParameters.getSource())
+ .setLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
+ .setMessage(format("Email '%s' is already used", email))
+ .setPublicMessage(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();
+ }
+
+ private static String getProviderIdOrProviderLogin(UserIdentity userIdentity) {
+ String providerId = userIdentity.getProviderId();
+ return providerId == null ? userIdentity.getProviderLogin() : providerId;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.authentication;
+
+import org.sonar.api.server.authentication.IdentityProvider;
+import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.server.authentication.event.AuthenticationEvent;
+
+import static java.util.Objects.requireNonNull;
+
+class UserRegistration {
+
+ /**
+ * Strategy to be executed when the email of the user is already used by another user
+ */
+ enum ExistingEmailStrategy {
+ /**
+ * Authentication is allowed, the email is moved from other user to current user
+ */
+ ALLOW,
+ /**
+ * Authentication process is stopped, the user is redirected to a page explaining that the email is already used
+ */
+ WARN,
+ /**
+ * Forbid authentication of the user
+ */
+ FORBID
+ }
+
+ /**
+ * Strategy to be executed when the login of the user is updated
+ */
+ enum UpdateLoginStrategy {
+ /**
+ * Authentication is allowed, the login of the user updated
+ */
+ ALLOW,
+ /**
+ * Authentication process is stopped, the user is redirected to a page explaining that the login will be updated.
+ * It only happens when personal organizations are activated
+ */
+ WARN
+ }
+
+ private final UserIdentity userIdentity;
+ private final IdentityProvider provider;
+ private final AuthenticationEvent.Source source;
+ private final ExistingEmailStrategy existingEmailStrategy;
+ private final UpdateLoginStrategy updateLoginStrategy;
+
+ UserRegistration(Builder builder) {
+ this.userIdentity = builder.userIdentity;
+ this.provider = builder.provider;
+ this.source = builder.source;
+ this.existingEmailStrategy = builder.existingEmailStrategy;
+ this.updateLoginStrategy = builder.updateLoginStrategy;
+ }
+
+ public UserIdentity getUserIdentity() {
+ return userIdentity;
+ }
+
+ public IdentityProvider getProvider() {
+ return provider;
+ }
+
+ public AuthenticationEvent.Source getSource() {
+ return source;
+ }
+
+ public ExistingEmailStrategy getExistingEmailStrategy() {
+ return existingEmailStrategy;
+ }
+
+ public UpdateLoginStrategy getUpdateLoginStrategy() {
+ return updateLoginStrategy;
+ }
+
+ static UserRegistration.Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private UserIdentity userIdentity;
+ private IdentityProvider provider;
+ private AuthenticationEvent.Source source;
+ private ExistingEmailStrategy existingEmailStrategy;
+ private UpdateLoginStrategy updateLoginStrategy;
+
+ public Builder setUserIdentity(UserIdentity userIdentity) {
+ this.userIdentity = userIdentity;
+ return this;
+ }
+
+ public Builder setProvider(IdentityProvider provider) {
+ this.provider = provider;
+ return this;
+ }
+
+ public Builder setSource(AuthenticationEvent.Source source) {
+ this.source = source;
+ return this;
+ }
+
+ /**
+ * Strategy to be executed when the email of the user is already used by another user
+ */
+ public Builder setExistingEmailStrategy(ExistingEmailStrategy existingEmailStrategy) {
+ this.existingEmailStrategy = existingEmailStrategy;
+ return this;
+ }
+
+ /**
+ * Strategy to be executed when the login of the user has changed
+ */
+ public Builder setUpdateLoginStrategy(UpdateLoginStrategy updateLoginStrategy) {
+ this.updateLoginStrategy = updateLoginStrategy;
+ return this;
+ }
+
+ public UserRegistration build() {
+ requireNonNull(userIdentity, "userIdentity must be set");
+ requireNonNull(provider, "identityProvider must be set");
+ requireNonNull(source, "Source must be set");
+ requireNonNull(existingEmailStrategy, "existingEmailStrategy must be set ");
+ requireNonNull(updateLoginStrategy, "updateLoginStrategy must be set");
+ return new UserRegistration(this);
+ }
+ }
+}
private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
- private TestUserIdentityAuthenticator userIdentityAuthenticator = new TestUserIdentityAuthenticator();
+ private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar();
private Server server = mock(Server.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
-import static org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy.FORBID;
+import static org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy.FORBID;
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;
private ExternalUsersProvider externalUsersProvider = mock(ExternalUsersProvider.class);
private ExternalGroupsProvider externalGroupsProvider = mock(ExternalGroupsProvider.class);
- private TestUserIdentityAuthenticator userIdentityAuthenticator = new TestUserIdentityAuthenticator();
+ private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar();
private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
- private UserIdentityAuthenticatorImpl userIdentityAuthenticator = new UserIdentityAuthenticatorImpl(
+ private UserRegistrarImpl userIdentityAuthenticator = new UserRegistrarImpl(
db.getDbClient(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider, organizationUpdater,
new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
import org.sonar.api.server.authentication.OAuth2IdentityProvider;
import org.sonar.api.server.authentication.UserIdentity;
import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
import org.sonar.server.user.TestUserSessionFactory;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSession;
public ExpectedException thrown = ExpectedException.none();
private ThreadLocalUserSession threadLocalUserSession = mock(ThreadLocalUserSession.class);
- private TestUserIdentityAuthenticator userIdentityAuthenticator = new TestUserIdentityAuthenticator();
+ private TestUserRegistrar userIdentityAuthenticator = new TestUserRegistrar();
private Server server = mock(Server.class);
private OAuthCsrfVerifier csrfVerifier = mock(OAuthCsrfVerifier.class);
private JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.authentication;
-
-import org.sonar.db.user.UserDto;
-import org.sonar.db.user.UserTesting;
-
-public class TestUserIdentityAuthenticator implements UserIdentityAuthenticator {
-
- private UserIdentityAuthenticatorParameters authenticatorParameters;
-
- @Override
- public UserDto authenticate(UserIdentityAuthenticatorParameters authenticatorParameters) {
- this.authenticatorParameters = authenticatorParameters;
- String providerId = authenticatorParameters.getUserIdentity().getProviderId();
- return UserTesting.newUserDto()
- .setLocal(false)
- .setLogin(authenticatorParameters.getUserIdentity().getLogin())
- .setExternalLogin(authenticatorParameters.getUserIdentity().getProviderLogin())
- .setExternalId(providerId == null ? authenticatorParameters.getUserIdentity().getProviderLogin() : providerId)
- .setExternalIdentityProvider(authenticatorParameters.getProvider().getKey());
- }
-
- boolean isAuthenticated() {
- return authenticatorParameters != null;
- }
-
- UserIdentityAuthenticatorParameters getAuthenticatorParameters() {
- return authenticatorParameters;
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.authentication;
+
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserTesting;
+
+public class TestUserRegistrar implements UserRegistrar {
+
+ private UserRegistration authenticatorParameters;
+
+ @Override
+ public UserDto register(UserRegistration registration) {
+ this.authenticatorParameters = registration;
+ String providerId = registration.getUserIdentity().getProviderId();
+ return UserTesting.newUserDto()
+ .setLocal(false)
+ .setLogin(registration.getUserIdentity().getLogin())
+ .setExternalLogin(registration.getUserIdentity().getProviderLogin())
+ .setExternalId(providerId == null ? registration.getUserIdentity().getProviderLogin() : providerId)
+ .setExternalIdentityProvider(registration.getProvider().getKey());
+ }
+
+ boolean isAuthenticated() {
+ return authenticatorParameters != null;
+ }
+
+ UserRegistration getAuthenticatorParameters() {
+ return authenticatorParameters;
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.authentication;
-
-import java.util.Optional;
-import java.util.stream.Collectors;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.resources.ResourceTypes;
-import org.sonar.api.server.authentication.UserIdentity;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
-import org.sonar.core.util.UuidFactoryFast;
-import org.sonar.core.util.stream.MoreCollectors;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.ResourceTypesRule;
-import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.UserDto;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy;
-import org.sonar.server.authentication.UserIdentityAuthenticatorParameters.UpdateLoginStrategy;
-import org.sonar.server.authentication.event.AuthenticationEvent;
-import org.sonar.server.authentication.event.AuthenticationEvent.Source;
-import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
-import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.organization.DefaultOrganizationProvider;
-import org.sonar.server.organization.OrganizationUpdater;
-import org.sonar.server.organization.OrganizationUpdaterImpl;
-import org.sonar.server.organization.OrganizationValidationImpl;
-import org.sonar.server.organization.TestDefaultOrganizationProvider;
-import org.sonar.server.organization.TestOrganizationFlags;
-import org.sonar.server.permission.PermissionService;
-import org.sonar.server.permission.PermissionServiceImpl;
-import org.sonar.server.user.NewUserNotifier;
-import org.sonar.server.user.UserUpdater;
-import org.sonar.server.user.index.UserIndexer;
-import org.sonar.server.usergroups.DefaultGroupFinder;
-
-import static com.google.common.collect.Sets.newHashSet;
-import static java.util.Arrays.stream;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS;
-import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.server.authentication.UserIdentityAuthenticatorParameters.ExistingEmailStrategy.FORBID;
-import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
-import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
-
-public class UserIdentityAuthenticatorImplTest {
-
- private static String USER_LOGIN = "github-johndoo";
-
- private static UserIdentity USER_IDENTITY = UserIdentity.builder()
- .setProviderId("ABCD")
- .setProviderLogin("johndoo")
- .setLogin(USER_LOGIN)
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
- private static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
- .setKey("github")
- .setName("name of github")
- .setEnabled(true)
- .setAllowsUsersToSignUp(true);
-
- private MapSettings settings = new MapSettings();
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
- @Rule
- public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
- @Rule
- public EsTester es = EsTester.create();
- private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
- private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
- private OrganizationUpdater organizationUpdater = mock(OrganizationUpdater.class);
- private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
- private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
- private UserUpdater userUpdater = new UserUpdater(
- mock(NewUserNotifier.class),
- db.getDbClient(),
- userIndexer,
- organizationFlags,
- defaultOrganizationProvider,
- organizationUpdater,
- new DefaultGroupFinder(db.getDbClient()),
- settings.asConfig(),
- localAuthentication);
-
- private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
- private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
-
- private UserIdentityAuthenticatorImpl underTest = new UserIdentityAuthenticatorImpl(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
- new OrganizationUpdaterImpl(db.getDbClient(), mock(System2.class), UuidFactoryFast.getInstance(),
- new OrganizationValidationImpl(), settings.asConfig(), null, null, null, permissionService),
- new DefaultGroupFinder(db.getDbClient()));
-
- @Test
- public void authenticate_new_user() {
- organizationFlags.setEnabled(true);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto user = db.users().selectUserByLogin(USER_LOGIN).get();
- assertThat(user).isNotNull();
- assertThat(user.isActive()).isTrue();
- assertThat(user.getName()).isEqualTo("John");
- assertThat(user.getEmail()).isEqualTo("john@email.com");
- assertThat(user.getExternalLogin()).isEqualTo("johndoo");
- assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
- assertThat(user.getExternalId()).isEqualTo("ABCD");
- assertThat(user.isRoot()).isFalse();
- checkGroupMembership(user);
- }
-
- @Test
- public void authenticate_new_user_generate_login_when_no_login_provided() {
- organizationFlags.setEnabled(true);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(UserIdentity.builder()
- .setProviderId("ABCD")
- .setProviderLogin("johndoo")
- .setName("John Doe")
- .setEmail("john@email.com")
- .build())
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto user = db.getDbClient().userDao().selectByEmail(db.getSession(), "john@email.com").get(0);
- assertThat(user).isNotNull();
- assertThat(user.isActive()).isTrue();
- assertThat(user.getLogin()).isNotEqualTo("John Doe").startsWith("john-doe");
- assertThat(user.getEmail()).isEqualTo("john@email.com");
- assertThat(user.getExternalLogin()).isEqualTo("johndoo");
- assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
- assertThat(user.getExternalId()).isEqualTo("ABCD");
- }
-
- @Test
- public void authenticate_new_user_with_groups() {
- organizationFlags.setEnabled(true);
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
-
- authenticate(USER_LOGIN, "group1", "group2", "group3");
-
- Optional<UserDto> user = db.users().selectUserByLogin(USER_LOGIN);
- checkGroupMembership(user.get(), group1, group2);
- }
-
- @Test
- public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() {
- organizationFlags.setEnabled(false);
- UserDto user = db.users().insertUser();
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto defaultGroup = insertDefaultGroup();
- db.users().insertMember(group1, user);
- db.users().insertMember(defaultGroup, user);
-
- authenticate(user.getLogin(), "group1");
-
- checkGroupMembership(user, group1, defaultGroup);
- }
-
- @Test
- public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() {
- organizationFlags.setEnabled(true);
- UserDto user = db.users().insertUser();
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto defaultGroup = insertDefaultGroup();
- db.users().insertMember(group1, user);
- db.users().insertMember(defaultGroup, user);
-
- authenticate(user.getLogin(), "group1");
-
- checkGroupMembership(user, group1);
- }
-
- @Test
- public void authenticate_new_user_sets_onboarded_flag_to_false_when_onboarding_setting_is_set_to_true() {
- organizationFlags.setEnabled(true);
- settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isFalse();
- }
-
- @Test
- public void authenticate_new_user_sets_onboarded_flag_to_true_when_onboarding_setting_is_set_to_false() {
- organizationFlags.setEnabled(true);
- settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isTrue();
- }
-
- @Test
- public void external_id_is_set_to_provider_login_when_null() {
- organizationFlags.setEnabled(true);
- UserIdentity newUser = UserIdentity.builder()
- .setProviderId(null)
- .setLogin("john")
- .setProviderLogin("johndoo")
- .setName("JOhn")
- .build();
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(newUser)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin(newUser.getLogin()).get())
- .extracting(UserDto::getLogin, UserDto::getExternalId, UserDto::getExternalLogin)
- .contains("john", "johndoo", "johndoo");
- }
-
- @Test
- public void authenticate_new_user_update_existing_user_email_when_strategy_is_ALLOW() {
- organizationFlags.setEnabled(true);
- UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
- UserIdentity newUser = UserIdentity.builder()
- .setProviderLogin("johndoo")
- .setLogin("new_login")
- .setName(existingUser.getName())
- .setEmail(existingUser.getEmail())
- .build();
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(newUser)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto newUserReloaded = db.users().selectUserByLogin(newUser.getLogin()).get();
- assertThat(newUserReloaded.getEmail()).isEqualTo(existingUser.getEmail());
- UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
- assertThat(existingUserReloaded.getEmail()).isNull();
- }
-
- @Test
- public void throw_EmailAlreadyExistException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_WARN() {
- organizationFlags.setEnabled(true);
- UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
- UserIdentity newUser = UserIdentity.builder()
- .setProviderLogin("johndoo")
- .setLogin("new_login")
- .setName(existingUser.getName())
- .setEmail(existingUser.getEmail())
- .build();
-
- expectedException.expect(EmailAlreadyExistsRedirectionException.class);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(newUser)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void throw_AuthenticationException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_FORBID() {
- db.users().insertUser(u -> u.setEmail("john@email.com"));
- Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());
-
- expectedException.expect(authenticationException().from(source)
- .withLogin(USER_IDENTITY.getProviderLogin())
- .andPublicMessage("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."));
- expectedException.expectMessage("Email 'john@email.com' is already used");
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(source)
- .setExistingEmailStrategy(FORBID)
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void throw_AuthenticationException_when_authenticating_new_user_and_email_already_exists_multiple_times() {
- db.users().insertUser(u -> u.setEmail("john@email.com"));
- db.users().insertUser(u -> u.setEmail("john@email.com"));
- Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());
-
- expectedException.expect(authenticationException().from(source)
- .withLogin(USER_IDENTITY.getProviderLogin())
- .andPublicMessage("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."));
- expectedException.expectMessage("Email 'john@email.com' is already used");
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(source)
- .setExistingEmailStrategy(FORBID)
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() {
- TestIdentityProvider identityProvider = new TestIdentityProvider()
- .setKey("github")
- .setName("Github")
- .setEnabled(true)
- .setAllowsUsersToSignUp(false);
- Source source = Source.realm(AuthenticationEvent.Method.FORM, identityProvider.getName());
-
- expectedException.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getProviderLogin()).andPublicMessage("'github' users are not allowed to sign up"));
- expectedException.expectMessage("User signup disabled for provider 'github'");
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(identityProvider)
- .setSource(source)
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void authenticate_and_update_existing_user_matching_login() {
- db.users().insertUser(u -> u
- .setLogin(USER_LOGIN)
- .setName("Old name")
- .setEmail("Old email")
- .setExternalId("old id")
- .setExternalLogin("old identity")
- .setExternalIdentityProvider("old provide"));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin(USER_LOGIN).get())
- .extracting(UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider, UserDto::isActive)
- .contains("John", "john@email.com", "ABCD", "johndoo", "github", true);
- }
-
- @Test
- public void authenticate_and_update_existing_user_matching_external_id() {
- UserDto user = db.users().insertUser(u -> u
- .setLogin("Old login")
- .setName("Old name")
- .setEmail("Old email")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin("Old login")).isNotPresent();
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
- UserDto::isActive)
- .contains(USER_LOGIN, "John", "john@email.com", "ABCD", "johndoo", "github", true);
- }
-
- @Test
- public void authenticate_existing_user_and_update_only_login() {
- UserDto user = db.users().insertUser(u -> u
- .setLogin("old login")
- .setName(USER_IDENTITY.getName())
- .setEmail(USER_IDENTITY.getEmail())
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.users().selectUserByLogin("Old login")).isNotPresent();
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
- UserDto::isActive)
- .containsExactlyInAnyOrder(USER_LOGIN, USER_IDENTITY.getName(), USER_IDENTITY.getEmail(), USER_IDENTITY.getProviderId(), USER_IDENTITY.getProviderLogin(),
- IDENTITY_PROVIDER.getKey(),
- true);
- }
-
- @Test
- public void authenticate_existing_user_and_update_only_identity_provider_key() {
- UserDto user = db.users().insertUser(u -> u
- .setLogin(USER_LOGIN)
- .setName(USER_IDENTITY.getName())
- .setEmail(USER_IDENTITY.getEmail())
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin(USER_IDENTITY.getProviderLogin())
- .setExternalIdentityProvider("old identity provider"));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
- UserDto::isActive)
- .containsExactlyInAnyOrder(USER_LOGIN, USER_IDENTITY.getName(), USER_IDENTITY.getEmail(), USER_IDENTITY.getProviderId(), USER_IDENTITY.getProviderLogin(),
- IDENTITY_PROVIDER.getKey(),
- true);
- }
-
- @Test
- public void authenticate_existing_user_matching_login_when_external_id_is_null() {
- UserDto user = db.users().insertUser(u -> u
- .setLogin(USER_LOGIN)
- .setName("Old name")
- .setEmail("Old email")
- .setExternalId("Old id")
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(UserIdentity.builder()
- .setProviderId(null)
- .setProviderLogin("johndoo")
- .setLogin(USER_LOGIN)
- .setName("John")
- .setEmail("john@email.com")
- .build())
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
- UserDto::isActive)
- .contains(user.getLogin(), "John", "john@email.com", "johndoo", "johndoo", "github", true);
- }
-
- @Test
- public void authenticate_existing_user_when_login_is_not_provided() {
- UserDto user = db.users().insertUser(u -> u.setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(UserIdentity.builder()
- .setProviderId(user.getExternalId())
- .setProviderLogin(user.getExternalLogin())
- // No login provided
- .setName(user.getName())
- .setEmail(user.getEmail())
- .build())
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- // No new user is created
- assertThat(db.countRowsOfTable(db.getSession(), "users")).isEqualTo(1);
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
- UserDto::isActive)
- .contains(user.getLogin(), user.getName(), user.getEmail(), user.getExternalId(), user.getExternalLogin(), user.getExternalIdentityProvider(), true);
- }
-
- @Test
- public void authenticate_existing_user_with_login_update_and_strategy_is_ALLOW() {
- UserDto user = db.users().insertUser(u -> u
- .setLogin("Old login")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getExternalLogin)
- .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
- }
-
- @Test
- public void authenticate_existing_user_with_login_update_and_personal_org_does_not_exits_and_strategy_is_WARN() {
- organizationFlags.setEnabled(true);
- UserDto user = db.users().insertUser(u -> u
- .setLogin("Old login")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
- .setOrganizationUuid(null));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.WARN)
- .build());
-
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getExternalLogin)
- .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
- }
-
- @Test
- public void throw_UpdateLoginRedirectionException_when_authenticating_with_login_update_and_personal_org_exists_and_strategy_is_WARN() {
- organizationFlags.setEnabled(true);
- OrganizationDto organization = db.organizations().insert(o -> o.setKey("Old login"));
- db.users().insertUser(u -> u
- .setLogin("Old login")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
- .setOrganizationUuid(organization.getUuid()));
-
- expectedException.expect(UpdateLoginRedirectionException.class);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.WARN)
- .build());
- }
-
- @Test
- public void authenticate_existing_user_and_update_personal_og_key_when_personal_org_exists_and_strategy_is_ALLOW() {
- organizationFlags.setEnabled(true);
- OrganizationDto personalOrganization = db.organizations().insert(o -> o.setKey("Old login"));
- UserDto user = db.users().insertUser(u -> u
- .setLogin("Old login")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
- .setOrganizationUuid(personalOrganization.getUuid()));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
- .extracting(UserDto::getLogin, UserDto::getExternalLogin)
- .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
- OrganizationDto organizationReloaded = db.getDbClient().organizationDao().selectByUuid(db.getSession(), personalOrganization.getUuid()).get();
- assertThat(organizationReloaded.getKey()).isEqualTo(USER_LOGIN);
- }
-
- @Test
- public void fail_to_authenticate_existing_user_when_personal_org_does_not_exist() {
- organizationFlags.setEnabled(true);
- db.users().insertUser(u -> u
- .setLogin("Old login")
- .setExternalId(USER_IDENTITY.getProviderId())
- .setExternalLogin("old identity")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
- .setOrganizationUuid("unknown"));
-
- expectedException.expect(IllegalStateException.class);
- expectedException.expectMessage("Cannot find personal organization uuid 'unknown' for user 'Old login'");
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void authenticate_existing_disabled_user() {
- organizationFlags.setEnabled(true);
- db.users().insertUser(u -> u
- .setLogin(USER_LOGIN)
- .setActive(false)
- .setName("Old name")
- .setEmail("Old email")
- .setExternalId("old id")
- .setExternalLogin("old identity")
- .setExternalIdentityProvider("old provide"));
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(USER_IDENTITY)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
- assertThat(userDto.isActive()).isTrue();
- assertThat(userDto.getName()).isEqualTo("John");
- assertThat(userDto.getEmail()).isEqualTo("john@email.com");
- assertThat(userDto.getExternalId()).isEqualTo("ABCD");
- assertThat(userDto.getExternalLogin()).isEqualTo("johndoo");
- assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
- assertThat(userDto.isRoot()).isFalse();
- }
-
- @Test
- public void authenticate_existing_user_when_email_already_exists_and_strategy_is_ALLOW() {
- organizationFlags.setEnabled(true);
- UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
- UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
- UserIdentity userIdentity = UserIdentity.builder()
- .setLogin(currentUser.getLogin())
- .setProviderLogin("johndoo")
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(userIdentity)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
- assertThat(currentUserReloaded.getEmail()).isEqualTo("john@email.com");
- UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
- assertThat(existingUserReloaded.getEmail()).isNull();
- }
-
- @Test
- public void throw_EmailAlreadyExistException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_WARN() {
- organizationFlags.setEnabled(true);
- UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
- UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
- UserIdentity userIdentity = UserIdentity.builder()
- .setLogin(currentUser.getLogin())
- .setProviderLogin("johndoo")
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
- expectedException.expect(EmailAlreadyExistsRedirectionException.class);
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(userIdentity)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void throw_AuthenticationException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_FORBID() {
- organizationFlags.setEnabled(true);
- UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
- UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
- UserIdentity userIdentity = UserIdentity.builder()
- .setLogin(currentUser.getLogin())
- .setProviderLogin("johndoo")
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
- expectedException.expect(authenticationException().from(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
- .withLogin(userIdentity.getProviderLogin())
- .andPublicMessage("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."));
- expectedException.expectMessage("Email 'john@email.com' is already used");
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(userIdentity)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- @Test
- public void does_not_fail_to_authenticate_user_when_email_has_not_changed_and_strategy_is_FORBID() {
- organizationFlags.setEnabled(true);
- UserDto currentUser = db.users().insertUser(u -> u.setEmail("john@email.com")
- .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
- UserIdentity userIdentity = UserIdentity.builder()
- .setLogin(currentUser.getLogin())
- .setProviderId(currentUser.getExternalId())
- .setProviderLogin(currentUser.getExternalLogin())
- .setName("John")
- .setEmail("john@email.com")
- .build();
-
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(userIdentity)
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
- assertThat(currentUserReloaded.getEmail()).isEqualTo("john@email.com");
- }
-
- @Test
- public void authenticate_existing_user_and_add_new_groups() {
- organizationFlags.setEnabled(true);
- UserDto user = db.users().insertUser(newUserDto()
- .setLogin(USER_LOGIN)
- .setActive(true)
- .setName("John"));
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
-
- authenticate(USER_LOGIN, "group1", "group2", "group3");
-
- checkGroupMembership(user, group1, group2);
- }
-
- @Test
- public void authenticate_existing_user_and_remove_groups() {
- organizationFlags.setEnabled(true);
- UserDto user = db.users().insertUser(newUserDto()
- .setLogin(USER_LOGIN)
- .setActive(true)
- .setName("John"));
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
- db.users().insertMember(group1, user);
- db.users().insertMember(group2, user);
-
- authenticate(USER_LOGIN, "group1");
-
- checkGroupMembership(user, group1);
- }
-
- @Test
- public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() {
- organizationFlags.setEnabled(false);
- UserDto user = db.users().insertUser();
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
- GroupDto defaultGroup = insertDefaultGroup();
- db.users().insertMember(group1, user);
- db.users().insertMember(group2, user);
- db.users().insertMember(defaultGroup, user);
-
- authenticate(user.getLogin());
-
- checkGroupMembership(user, defaultGroup);
- }
-
- @Test
- public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() {
- organizationFlags.setEnabled(true);
- UserDto user = db.users().insertUser();
- GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
- GroupDto defaultGroup = insertDefaultGroup();
- db.users().insertMember(group1, user);
- db.users().insertMember(defaultGroup, user);
-
- authenticate(user.getLogin(), "group1");
-
- checkGroupMembership(user, group1);
- }
-
- @Test
- public void ignore_groups_on_non_default_organizations() {
- organizationFlags.setEnabled(true);
- OrganizationDto org = db.organizations().insert();
- UserDto user = db.users().insertUser(newUserDto()
- .setLogin(USER_LOGIN)
- .setActive(true)
- .setName("John"));
- String groupName = "a-group";
- GroupDto groupInDefaultOrg = db.users().insertGroup(db.getDefaultOrganization(), groupName);
- GroupDto groupInOrg = db.users().insertGroup(org, groupName);
-
- // adding a group with the same name than in non-default organization
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(UserIdentity.builder()
- .setProviderLogin("johndoo")
- .setLogin(user.getLogin())
- .setName(user.getName())
- .setGroups(newHashSet(groupName))
- .build())
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
-
- checkGroupMembership(user, groupInDefaultOrg);
- }
-
- private void authenticate(String login, String... groups) {
- underTest.authenticate(UserIdentityAuthenticatorParameters.builder()
- .setUserIdentity(UserIdentity.builder()
- .setProviderLogin("johndoo")
- .setLogin(login)
- .setName("John")
- // No group
- .setGroups(stream(groups).collect(MoreCollectors.toSet()))
- .build())
- .setProvider(IDENTITY_PROVIDER)
- .setSource(Source.local(BASIC))
- .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
- .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
- .build());
- }
-
- private void checkGroupMembership(UserDto user, GroupDto... expectedGroups) {
- assertThat(db.users().selectGroupIdsOfUser(user)).containsOnly(stream(expectedGroups).map(GroupDto::getId).collect(Collectors.toList()).toArray(new Integer[] {}));
- }
-
- private GroupDto insertDefaultGroup() {
- return db.users().insertDefaultGroup(db.getDefaultOrganization(), "sonar-users");
- }
-
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.authentication;
+
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.authentication.UserIdentity;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy;
+import org.sonar.server.authentication.UserRegistration.UpdateLoginStrategy;
+import org.sonar.server.authentication.event.AuthenticationEvent;
+import org.sonar.server.authentication.event.AuthenticationEvent.Source;
+import org.sonar.server.authentication.exception.EmailAlreadyExistsRedirectionException;
+import org.sonar.server.authentication.exception.UpdateLoginRedirectionException;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.organization.OrganizationUpdater;
+import org.sonar.server.organization.OrganizationUpdaterImpl;
+import org.sonar.server.organization.OrganizationValidationImpl;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+import org.sonar.server.organization.TestOrganizationFlags;
+import org.sonar.server.permission.PermissionService;
+import org.sonar.server.permission.PermissionServiceImpl;
+import org.sonar.server.user.NewUserNotifier;
+import org.sonar.server.user.UserUpdater;
+import org.sonar.server.user.index.UserIndexer;
+import org.sonar.server.usergroups.DefaultGroupFinder;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS;
+import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.authentication.UserRegistration.ExistingEmailStrategy.FORBID;
+import static org.sonar.server.authentication.event.AuthenticationEvent.Method.BASIC;
+import static org.sonar.server.authentication.event.AuthenticationExceptionMatcher.authenticationException;
+
+public class UserRegistrarImplTest {
+
+ private static String USER_LOGIN = "github-johndoo";
+
+ private static UserIdentity USER_IDENTITY = UserIdentity.builder()
+ .setProviderId("ABCD")
+ .setProviderLogin("johndoo")
+ .setLogin(USER_LOGIN)
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
+ private static TestIdentityProvider IDENTITY_PROVIDER = new TestIdentityProvider()
+ .setKey("github")
+ .setName("name of github")
+ .setEnabled(true)
+ .setAllowsUsersToSignUp(true);
+
+ private MapSettings settings = new MapSettings();
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public DbTester db = DbTester.create(new AlwaysIncreasingSystem2());
+ @Rule
+ public EsTester es = EsTester.create();
+ private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
+ private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
+ private OrganizationUpdater organizationUpdater = mock(OrganizationUpdater.class);
+ private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
+ private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
+ private UserUpdater userUpdater = new UserUpdater(
+ mock(NewUserNotifier.class),
+ db.getDbClient(),
+ userIndexer,
+ organizationFlags,
+ defaultOrganizationProvider,
+ organizationUpdater,
+ new DefaultGroupFinder(db.getDbClient()),
+ settings.asConfig(),
+ localAuthentication);
+
+ private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
+ private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
+
+ private UserRegistrarImpl underTest = new UserRegistrarImpl(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
+ new OrganizationUpdaterImpl(db.getDbClient(), mock(System2.class), UuidFactoryFast.getInstance(),
+ new OrganizationValidationImpl(), settings.asConfig(), null, null, null, permissionService),
+ new DefaultGroupFinder(db.getDbClient()));
+
+ @Test
+ public void authenticate_new_user() {
+ organizationFlags.setEnabled(true);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto user = db.users().selectUserByLogin(USER_LOGIN).get();
+ assertThat(user).isNotNull();
+ assertThat(user.isActive()).isTrue();
+ assertThat(user.getName()).isEqualTo("John");
+ assertThat(user.getEmail()).isEqualTo("john@email.com");
+ assertThat(user.getExternalLogin()).isEqualTo("johndoo");
+ assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
+ assertThat(user.getExternalId()).isEqualTo("ABCD");
+ assertThat(user.isRoot()).isFalse();
+ checkGroupMembership(user);
+ }
+
+ @Test
+ public void authenticate_new_user_generate_login_when_no_login_provided() {
+ organizationFlags.setEnabled(true);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(UserIdentity.builder()
+ .setProviderId("ABCD")
+ .setProviderLogin("johndoo")
+ .setName("John Doe")
+ .setEmail("john@email.com")
+ .build())
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.realm(BASIC, IDENTITY_PROVIDER.getName()))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto user = db.getDbClient().userDao().selectByEmail(db.getSession(), "john@email.com").get(0);
+ assertThat(user).isNotNull();
+ assertThat(user.isActive()).isTrue();
+ assertThat(user.getLogin()).isNotEqualTo("John Doe").startsWith("john-doe");
+ assertThat(user.getEmail()).isEqualTo("john@email.com");
+ assertThat(user.getExternalLogin()).isEqualTo("johndoo");
+ assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
+ assertThat(user.getExternalId()).isEqualTo("ABCD");
+ }
+
+ @Test
+ public void authenticate_new_user_with_groups() {
+ organizationFlags.setEnabled(true);
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
+
+ authenticate(USER_LOGIN, "group1", "group2", "group3");
+
+ Optional<UserDto> user = db.users().selectUserByLogin(USER_LOGIN);
+ checkGroupMembership(user.get(), group1, group2);
+ }
+
+ @Test
+ public void authenticate_new_user_and_force_default_group_when_organizations_are_disabled() {
+ organizationFlags.setEnabled(false);
+ UserDto user = db.users().insertUser();
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto defaultGroup = insertDefaultGroup();
+ db.users().insertMember(group1, user);
+ db.users().insertMember(defaultGroup, user);
+
+ authenticate(user.getLogin(), "group1");
+
+ checkGroupMembership(user, group1, defaultGroup);
+ }
+
+ @Test
+ public void does_not_force_default_group_when_authenticating_new_user_if_organizations_are_enabled() {
+ organizationFlags.setEnabled(true);
+ UserDto user = db.users().insertUser();
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto defaultGroup = insertDefaultGroup();
+ db.users().insertMember(group1, user);
+ db.users().insertMember(defaultGroup, user);
+
+ authenticate(user.getLogin(), "group1");
+
+ checkGroupMembership(user, group1);
+ }
+
+ @Test
+ public void authenticate_new_user_sets_onboarded_flag_to_false_when_onboarding_setting_is_set_to_true() {
+ organizationFlags.setEnabled(true);
+ settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, true);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isFalse();
+ }
+
+ @Test
+ public void authenticate_new_user_sets_onboarded_flag_to_true_when_onboarding_setting_is_set_to_false() {
+ organizationFlags.setEnabled(true);
+ settings.setProperty(ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS, false);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin(USER_LOGIN).get().isOnboarded()).isTrue();
+ }
+
+ @Test
+ public void external_id_is_set_to_provider_login_when_null() {
+ organizationFlags.setEnabled(true);
+ UserIdentity newUser = UserIdentity.builder()
+ .setProviderId(null)
+ .setLogin("john")
+ .setProviderLogin("johndoo")
+ .setName("JOhn")
+ .build();
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(newUser)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin(newUser.getLogin()).get())
+ .extracting(UserDto::getLogin, UserDto::getExternalId, UserDto::getExternalLogin)
+ .contains("john", "johndoo", "johndoo");
+ }
+
+ @Test
+ public void authenticate_new_user_update_existing_user_email_when_strategy_is_ALLOW() {
+ organizationFlags.setEnabled(true);
+ UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
+ UserIdentity newUser = UserIdentity.builder()
+ .setProviderLogin("johndoo")
+ .setLogin("new_login")
+ .setName(existingUser.getName())
+ .setEmail(existingUser.getEmail())
+ .build();
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(newUser)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto newUserReloaded = db.users().selectUserByLogin(newUser.getLogin()).get();
+ assertThat(newUserReloaded.getEmail()).isEqualTo(existingUser.getEmail());
+ UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
+ assertThat(existingUserReloaded.getEmail()).isNull();
+ }
+
+ @Test
+ public void throw_EmailAlreadyExistException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_WARN() {
+ organizationFlags.setEnabled(true);
+ UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
+ UserIdentity newUser = UserIdentity.builder()
+ .setProviderLogin("johndoo")
+ .setLogin("new_login")
+ .setName(existingUser.getName())
+ .setEmail(existingUser.getEmail())
+ .build();
+
+ expectedException.expect(EmailAlreadyExistsRedirectionException.class);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(newUser)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void throw_AuthenticationException_when_authenticating_new_user_when_email_already_exists_and_strategy_is_FORBID() {
+ db.users().insertUser(u -> u.setEmail("john@email.com"));
+ Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());
+
+ expectedException.expect(authenticationException().from(source)
+ .withLogin(USER_IDENTITY.getProviderLogin())
+ .andPublicMessage("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."));
+ expectedException.expectMessage("Email 'john@email.com' is already used");
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(source)
+ .setExistingEmailStrategy(FORBID)
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void throw_AuthenticationException_when_authenticating_new_user_and_email_already_exists_multiple_times() {
+ db.users().insertUser(u -> u.setEmail("john@email.com"));
+ db.users().insertUser(u -> u.setEmail("john@email.com"));
+ Source source = Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName());
+
+ expectedException.expect(authenticationException().from(source)
+ .withLogin(USER_IDENTITY.getProviderLogin())
+ .andPublicMessage("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."));
+ expectedException.expectMessage("Email 'john@email.com' is already used");
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(source)
+ .setExistingEmailStrategy(FORBID)
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void fail_to_authenticate_new_user_when_allow_users_to_signup_is_false() {
+ TestIdentityProvider identityProvider = new TestIdentityProvider()
+ .setKey("github")
+ .setName("Github")
+ .setEnabled(true)
+ .setAllowsUsersToSignUp(false);
+ Source source = Source.realm(AuthenticationEvent.Method.FORM, identityProvider.getName());
+
+ expectedException.expect(authenticationException().from(source).withLogin(USER_IDENTITY.getProviderLogin()).andPublicMessage("'github' users are not allowed to sign up"));
+ expectedException.expectMessage("User signup disabled for provider 'github'");
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(identityProvider)
+ .setSource(source)
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void authenticate_and_update_existing_user_matching_login() {
+ db.users().insertUser(u -> u
+ .setLogin(USER_LOGIN)
+ .setName("Old name")
+ .setEmail("Old email")
+ .setExternalId("old id")
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider("old provide"));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin(USER_LOGIN).get())
+ .extracting(UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider, UserDto::isActive)
+ .contains("John", "john@email.com", "ABCD", "johndoo", "github", true);
+ }
+
+ @Test
+ public void authenticate_and_update_existing_user_matching_external_id() {
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setName("Old name")
+ .setEmail("Old email")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin("Old login")).isNotPresent();
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
+ UserDto::isActive)
+ .contains(USER_LOGIN, "John", "john@email.com", "ABCD", "johndoo", "github", true);
+ }
+
+ @Test
+ public void authenticate_existing_user_and_update_only_login() {
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin("old login")
+ .setName(USER_IDENTITY.getName())
+ .setEmail(USER_IDENTITY.getEmail())
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.users().selectUserByLogin("Old login")).isNotPresent();
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
+ UserDto::isActive)
+ .containsExactlyInAnyOrder(USER_LOGIN, USER_IDENTITY.getName(), USER_IDENTITY.getEmail(), USER_IDENTITY.getProviderId(), USER_IDENTITY.getProviderLogin(),
+ IDENTITY_PROVIDER.getKey(),
+ true);
+ }
+
+ @Test
+ public void authenticate_existing_user_and_update_only_identity_provider_key() {
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin(USER_LOGIN)
+ .setName(USER_IDENTITY.getName())
+ .setEmail(USER_IDENTITY.getEmail())
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin(USER_IDENTITY.getProviderLogin())
+ .setExternalIdentityProvider("old identity provider"));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
+ UserDto::isActive)
+ .containsExactlyInAnyOrder(USER_LOGIN, USER_IDENTITY.getName(), USER_IDENTITY.getEmail(), USER_IDENTITY.getProviderId(), USER_IDENTITY.getProviderLogin(),
+ IDENTITY_PROVIDER.getKey(),
+ true);
+ }
+
+ @Test
+ public void authenticate_existing_user_matching_login_when_external_id_is_null() {
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin(USER_LOGIN)
+ .setName("Old name")
+ .setEmail("Old email")
+ .setExternalId("Old id")
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(UserIdentity.builder()
+ .setProviderId(null)
+ .setProviderLogin("johndoo")
+ .setLogin(USER_LOGIN)
+ .setName("John")
+ .setEmail("john@email.com")
+ .build())
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
+ UserDto::isActive)
+ .contains(user.getLogin(), "John", "john@email.com", "johndoo", "johndoo", "github", true);
+ }
+
+ @Test
+ public void authenticate_existing_user_when_login_is_not_provided() {
+ UserDto user = db.users().insertUser(u -> u.setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(UserIdentity.builder()
+ .setProviderId(user.getExternalId())
+ .setProviderLogin(user.getExternalLogin())
+ // No login provided
+ .setName(user.getName())
+ .setEmail(user.getEmail())
+ .build())
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ // No new user is created
+ assertThat(db.countRowsOfTable(db.getSession(), "users")).isEqualTo(1);
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getName, UserDto::getEmail, UserDto::getExternalId, UserDto::getExternalLogin, UserDto::getExternalIdentityProvider,
+ UserDto::isActive)
+ .contains(user.getLogin(), user.getName(), user.getEmail(), user.getExternalId(), user.getExternalLogin(), user.getExternalIdentityProvider(), true);
+ }
+
+ @Test
+ public void authenticate_existing_user_with_login_update_and_strategy_is_ALLOW() {
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getExternalLogin)
+ .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
+ }
+
+ @Test
+ public void authenticate_existing_user_with_login_update_and_personal_org_does_not_exits_and_strategy_is_WARN() {
+ organizationFlags.setEnabled(true);
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
+ .setOrganizationUuid(null));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.WARN)
+ .build());
+
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getExternalLogin)
+ .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
+ }
+
+ @Test
+ public void throw_UpdateLoginRedirectionException_when_authenticating_with_login_update_and_personal_org_exists_and_strategy_is_WARN() {
+ organizationFlags.setEnabled(true);
+ OrganizationDto organization = db.organizations().insert(o -> o.setKey("Old login"));
+ db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
+ .setOrganizationUuid(organization.getUuid()));
+
+ expectedException.expect(UpdateLoginRedirectionException.class);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.WARN)
+ .build());
+ }
+
+ @Test
+ public void authenticate_existing_user_and_update_personal_og_key_when_personal_org_exists_and_strategy_is_ALLOW() {
+ organizationFlags.setEnabled(true);
+ OrganizationDto personalOrganization = db.organizations().insert(o -> o.setKey("Old login"));
+ UserDto user = db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
+ .setOrganizationUuid(personalOrganization.getUuid()));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ assertThat(db.getDbClient().userDao().selectByUuid(db.getSession(), user.getUuid()))
+ .extracting(UserDto::getLogin, UserDto::getExternalLogin)
+ .contains(USER_LOGIN, USER_IDENTITY.getProviderLogin());
+ OrganizationDto organizationReloaded = db.getDbClient().organizationDao().selectByUuid(db.getSession(), personalOrganization.getUuid()).get();
+ assertThat(organizationReloaded.getKey()).isEqualTo(USER_LOGIN);
+ }
+
+ @Test
+ public void fail_to_authenticate_existing_user_when_personal_org_does_not_exist() {
+ organizationFlags.setEnabled(true);
+ db.users().insertUser(u -> u
+ .setLogin("Old login")
+ .setExternalId(USER_IDENTITY.getProviderId())
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey())
+ .setOrganizationUuid("unknown"));
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Cannot find personal organization uuid 'unknown' for user 'Old login'");
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void authenticate_existing_disabled_user() {
+ organizationFlags.setEnabled(true);
+ db.users().insertUser(u -> u
+ .setLogin(USER_LOGIN)
+ .setActive(false)
+ .setName("Old name")
+ .setEmail("Old email")
+ .setExternalId("old id")
+ .setExternalLogin("old identity")
+ .setExternalIdentityProvider("old provide"));
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(USER_IDENTITY)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto userDto = db.users().selectUserByLogin(USER_LOGIN).get();
+ assertThat(userDto.isActive()).isTrue();
+ assertThat(userDto.getName()).isEqualTo("John");
+ assertThat(userDto.getEmail()).isEqualTo("john@email.com");
+ assertThat(userDto.getExternalId()).isEqualTo("ABCD");
+ assertThat(userDto.getExternalLogin()).isEqualTo("johndoo");
+ assertThat(userDto.getExternalIdentityProvider()).isEqualTo("github");
+ assertThat(userDto.isRoot()).isFalse();
+ }
+
+ @Test
+ public void authenticate_existing_user_when_email_already_exists_and_strategy_is_ALLOW() {
+ organizationFlags.setEnabled(true);
+ UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
+ UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
+ UserIdentity userIdentity = UserIdentity.builder()
+ .setLogin(currentUser.getLogin())
+ .setProviderLogin("johndoo")
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(userIdentity)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.ALLOW)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
+ assertThat(currentUserReloaded.getEmail()).isEqualTo("john@email.com");
+ UserDto existingUserReloaded = db.users().selectUserByLogin(existingUser.getLogin()).get();
+ assertThat(existingUserReloaded.getEmail()).isNull();
+ }
+
+ @Test
+ public void throw_EmailAlreadyExistException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_WARN() {
+ organizationFlags.setEnabled(true);
+ UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
+ UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
+ UserIdentity userIdentity = UserIdentity.builder()
+ .setLogin(currentUser.getLogin())
+ .setProviderLogin("johndoo")
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
+ expectedException.expect(EmailAlreadyExistsRedirectionException.class);
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(userIdentity)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.WARN)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void throw_AuthenticationException_when_authenticating_existing_user_when_email_already_exists_and_strategy_is_FORBID() {
+ organizationFlags.setEnabled(true);
+ UserDto existingUser = db.users().insertUser(u -> u.setEmail("john@email.com"));
+ UserDto currentUser = db.users().insertUser(u -> u.setEmail(null));
+ UserIdentity userIdentity = UserIdentity.builder()
+ .setLogin(currentUser.getLogin())
+ .setProviderLogin("johndoo")
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
+ expectedException.expect(authenticationException().from(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
+ .withLogin(userIdentity.getProviderLogin())
+ .andPublicMessage("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."));
+ expectedException.expectMessage("Email 'john@email.com' is already used");
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(userIdentity)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.realm(AuthenticationEvent.Method.FORM, IDENTITY_PROVIDER.getName()))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ @Test
+ public void does_not_fail_to_authenticate_user_when_email_has_not_changed_and_strategy_is_FORBID() {
+ organizationFlags.setEnabled(true);
+ UserDto currentUser = db.users().insertUser(u -> u.setEmail("john@email.com")
+ .setExternalIdentityProvider(IDENTITY_PROVIDER.getKey()));
+ UserIdentity userIdentity = UserIdentity.builder()
+ .setLogin(currentUser.getLogin())
+ .setProviderId(currentUser.getExternalId())
+ .setProviderLogin(currentUser.getExternalLogin())
+ .setName("John")
+ .setEmail("john@email.com")
+ .build();
+
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(userIdentity)
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ UserDto currentUserReloaded = db.users().selectUserByLogin(currentUser.getLogin()).get();
+ assertThat(currentUserReloaded.getEmail()).isEqualTo("john@email.com");
+ }
+
+ @Test
+ public void authenticate_existing_user_and_add_new_groups() {
+ organizationFlags.setEnabled(true);
+ UserDto user = db.users().insertUser(newUserDto()
+ .setLogin(USER_LOGIN)
+ .setActive(true)
+ .setName("John"));
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
+
+ authenticate(USER_LOGIN, "group1", "group2", "group3");
+
+ checkGroupMembership(user, group1, group2);
+ }
+
+ @Test
+ public void authenticate_existing_user_and_remove_groups() {
+ organizationFlags.setEnabled(true);
+ UserDto user = db.users().insertUser(newUserDto()
+ .setLogin(USER_LOGIN)
+ .setActive(true)
+ .setName("John"));
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
+ db.users().insertMember(group1, user);
+ db.users().insertMember(group2, user);
+
+ authenticate(USER_LOGIN, "group1");
+
+ checkGroupMembership(user, group1);
+ }
+
+ @Test
+ public void authenticate_existing_user_and_remove_all_groups_expect_default_when_organizations_are_disabled() {
+ organizationFlags.setEnabled(false);
+ UserDto user = db.users().insertUser();
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto group2 = db.users().insertGroup(db.getDefaultOrganization(), "group2");
+ GroupDto defaultGroup = insertDefaultGroup();
+ db.users().insertMember(group1, user);
+ db.users().insertMember(group2, user);
+ db.users().insertMember(defaultGroup, user);
+
+ authenticate(user.getLogin());
+
+ checkGroupMembership(user, defaultGroup);
+ }
+
+ @Test
+ public void does_not_force_default_group_when_authenticating_existing_user_when_organizations_are_enabled() {
+ organizationFlags.setEnabled(true);
+ UserDto user = db.users().insertUser();
+ GroupDto group1 = db.users().insertGroup(db.getDefaultOrganization(), "group1");
+ GroupDto defaultGroup = insertDefaultGroup();
+ db.users().insertMember(group1, user);
+ db.users().insertMember(defaultGroup, user);
+
+ authenticate(user.getLogin(), "group1");
+
+ checkGroupMembership(user, group1);
+ }
+
+ @Test
+ public void ignore_groups_on_non_default_organizations() {
+ organizationFlags.setEnabled(true);
+ OrganizationDto org = db.organizations().insert();
+ UserDto user = db.users().insertUser(newUserDto()
+ .setLogin(USER_LOGIN)
+ .setActive(true)
+ .setName("John"));
+ String groupName = "a-group";
+ GroupDto groupInDefaultOrg = db.users().insertGroup(db.getDefaultOrganization(), groupName);
+ GroupDto groupInOrg = db.users().insertGroup(org, groupName);
+
+ // adding a group with the same name than in non-default organization
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(UserIdentity.builder()
+ .setProviderLogin("johndoo")
+ .setLogin(user.getLogin())
+ .setName(user.getName())
+ .setGroups(newHashSet(groupName))
+ .build())
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+
+ checkGroupMembership(user, groupInDefaultOrg);
+ }
+
+ private void authenticate(String login, String... groups) {
+ underTest.register(UserRegistration.builder()
+ .setUserIdentity(UserIdentity.builder()
+ .setProviderLogin("johndoo")
+ .setLogin(login)
+ .setName("John")
+ // No group
+ .setGroups(stream(groups).collect(MoreCollectors.toSet()))
+ .build())
+ .setProvider(IDENTITY_PROVIDER)
+ .setSource(Source.local(BASIC))
+ .setExistingEmailStrategy(ExistingEmailStrategy.FORBID)
+ .setUpdateLoginStrategy(UpdateLoginStrategy.ALLOW)
+ .build());
+ }
+
+ private void checkGroupMembership(UserDto user, GroupDto... expectedGroups) {
+ assertThat(db.users().selectGroupIdsOfUser(user)).containsOnly(stream(expectedGroups).map(GroupDto::getId).collect(Collectors.toList()).toArray(new Integer[] {}));
+ }
+
+ private GroupDto insertDefaultGroup() {
+ return db.users().insertDefaultGroup(db.getDefaultOrganization(), "sonar-users");
+ }
+
+}