]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-213 rename UserIdentityAuthenticator
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 30 Nov 2018 12:58:26 +0000 (13:58 +0100)
committerSonarTech <sonartech@sonarsource.com>
Wed, 12 Dec 2018 19:21:02 +0000 (20:21 +0100)
to UserRegistrar to help understanding its responsibility.

19 files changed:
server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
server/sonar-server/src/main/java/org/sonar/server/authentication/BaseContextFactory.java
server/sonar-server/src/main/java/org/sonar/server/authentication/CredentialsExternalAuthentication.java
server/sonar-server/src/main/java/org/sonar/server/authentication/HttpHeadersAuthentication.java
server/sonar-server/src/main/java/org/sonar/server/authentication/OAuth2ContextFactory.java
server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorImpl.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorParameters.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrar.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistration.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/authentication/BaseContextFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/CredentialsExternalAuthenticationTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/OAuth2ContextFactoryTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserIdentityAuthenticator.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserRegistrar.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorImplTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java [new file with mode: 0644]

index ccdf9bbfea88be59eb36da3273f116e57b8b24ea..770f18d47a84441ec1f0803befb34b443180bc65 100644 (file)
@@ -37,7 +37,7 @@ public class AuthenticationModule extends Module {
       IdentityProviderRepository.class,
       BaseContextFactory.class,
       OAuth2ContextFactory.class,
-      UserIdentityAuthenticatorImpl.class,
+      UserRegistrarImpl.class,
       OAuthCsrfVerifier.class,
       UserSessionInitializer.class,
       JwtSerializer.class,
index 686dcd83138ab205c47a0c67895c257d0631b206..6aa0ac3c6f81976763fb764b27c0c32124db2222 100644 (file)
@@ -25,8 +25,8 @@ import org.sonar.api.platform.Server;
 import org.sonar.api.server.authentication.BaseIdentityProvider;
 import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.db.user.UserDto;
-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;
@@ -34,15 +34,15 @@ 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;
@@ -80,8 +80,8 @@ public class BaseContextFactory {
 
     @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))
index 0f5edacfe63bd7bef3c925579eb7af85bcd95b2a..d6cbd92ca359c9f85565cb4161353df867b9a970 100644 (file)
@@ -37,8 +37,8 @@ import org.sonar.api.server.authentication.UserIdentity;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.db.user.UserDto;
-import org.sonar.server.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;
@@ -58,7 +58,7 @@ public class CredentialsExternalAuthentication implements Startable {
 
   private final Configuration config;
   private final SecurityRealmFactory securityRealmFactory;
-  private final UserIdentityAuthenticator userIdentityAuthenticator;
+  private final UserRegistrar userRegistrar;
   private final AuthenticationEvent authenticationEvent;
 
   private SecurityRealm realm;
@@ -67,10 +67,10 @@ public class CredentialsExternalAuthentication implements Startable {
   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;
   }
 
@@ -143,8 +143,8 @@ public class CredentialsExternalAuthentication implements Startable {
       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))
index 11ddd8377fb2c2f33c331bc8878def8f2beac53a..6b42ee427ab58f47b9791bf20ac5850d680fdfee 100644 (file)
@@ -42,8 +42,8 @@ import org.sonar.api.utils.log.Logger;
 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;
@@ -70,7 +70,7 @@ public class HttpHeadersAuthentication implements Startable {
 
   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,
@@ -79,18 +79,18 @@ public class HttpHeadersAuthentication implements Startable {
 
   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;
   }
@@ -100,7 +100,7 @@ public class HttpHeadersAuthentication implements Startable {
     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())));
     }
   }
 
@@ -166,8 +166,8 @@ public class HttpHeadersAuthentication implements Startable {
       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())
index 26549396c6d129021c93ac001fd5f0216f0d9ec9..1fd9118349f549035b180448ad547060f1a4bc08 100644 (file)
@@ -28,8 +28,8 @@ import org.sonar.api.server.ServerSide;
 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;
@@ -41,17 +41,17 @@ import static org.sonar.server.authentication.OAuth2CallbackFilter.CALLBACK_PATH
 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;
@@ -133,8 +133,8 @@ public class OAuth2ContextFactory {
     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))
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticator.java
deleted file mode 100644 (file)
index d56ad68..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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);
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorImpl.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorImpl.java
deleted file mode 100644 (file)
index be21b9b..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * 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;
-  }
-
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorParameters.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserIdentityAuthenticatorParameters.java
deleted file mode 100644 (file)
index 224946e..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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);
-    }
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrar.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrar.java
new file mode 100644 (file)
index 0000000..a57b77c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java
new file mode 100644 (file)
index 0000000..7a89684
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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;
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistration.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/UserRegistration.java
new file mode 100644 (file)
index 0000000..eda9af3
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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);
+    }
+  }
+}
index 70cd7748e26917b9eed0bc7e45f128d3ee949de1..e939a1580d7c5bc9e73bb0714b134cb86489658c 100644 (file)
@@ -54,7 +54,7 @@ public class BaseContextFactoryTest {
 
   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);
index e0aed1011888aa54b52381923720fd64cb11a0d2..221c31c55ca78ea9a2bbc4b84346b34b10adb763 100644 (file)
@@ -44,7 +44,7 @@ import static org.mockito.Mockito.verify;
 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;
@@ -67,7 +67,7 @@ public class CredentialsExternalAuthenticationTest {
   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);
index e21f7eebb861eab1bf2c11bbfb665be1004052ba..ee0033eaf2e3f9cc0b25cbea96f43788e17baf3c 100644 (file)
@@ -103,7 +103,7 @@ public class HttpHeadersAuthenticationTest {
   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),
index 77dc77f459e313af9c9447220fdc59c91bd8a478..6836f19c87281b735b42ee9cfe03864bcdff1f7a 100644 (file)
@@ -32,8 +32,8 @@ import org.sonar.api.platform.Server;
 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;
@@ -62,7 +62,7 @@ public class OAuth2ContextFactoryTest {
   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);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserIdentityAuthenticator.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserIdentityAuthenticator.java
deleted file mode 100644 (file)
index bfb42c3..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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;
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserRegistrar.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/TestUserRegistrar.java
new file mode 100644 (file)
index 0000000..6bfe496
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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;
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorImplTest.java
deleted file mode 100644 (file)
index d054ddb..0000000
+++ /dev/null
@@ -1,894 +0,0 @@
-/*
- * 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");
-  }
-
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java
new file mode 100644 (file)
index 0000000..0950f66
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * 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");
+  }
+
+}