]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18655 Prevent account creation at login when the instance is managed
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>
Thu, 9 Mar 2023 17:43:53 +0000 (18:43 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 22 Mar 2023 20:04:07 +0000 (20:04 +0000)
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistration.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/HttpHeadersAuthenticationTest.java
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/UserRegistrarImplTest.java

index 9ecd8f384373922d02a8f54c3b9a7fa5a6878ca5..8ac82b36844f85c9b503a7b632eb063f168bd1cd 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserGroupDto;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.authentication.event.AuthenticationException;
+import org.sonar.server.management.ManagedInstanceService;
 import org.sonar.server.user.ExternalIdentity;
 import org.sonar.server.user.NewUser;
 import org.sonar.server.user.UpdateUser;
@@ -64,11 +65,14 @@ public class UserRegistrarImpl implements UserRegistrar {
   private final DbClient dbClient;
   private final UserUpdater userUpdater;
   private final DefaultGroupFinder defaultGroupFinder;
+  private final ManagedInstanceService managedInstanceService;
 
-  public UserRegistrarImpl(DbClient dbClient, UserUpdater userUpdater, DefaultGroupFinder defaultGroupFinder) {
+  public UserRegistrarImpl(DbClient dbClient, UserUpdater userUpdater, DefaultGroupFinder defaultGroupFinder,
+    ManagedInstanceService managedInstanceService) {
     this.dbClient = dbClient;
     this.userUpdater = userUpdater;
     this.defaultGroupFinder = defaultGroupFinder;
+    this.managedInstanceService = managedInstanceService;
   }
 
   @Override
@@ -159,6 +163,7 @@ public class UserRegistrarImpl implements UserRegistrar {
   }
 
   private UserDto registerNewUser(DbSession dbSession, @Nullable UserDto disabledUser, UserRegistration authenticatorParameters) {
+    blockUnmanagedUserCreationOnManagedInstance(authenticatorParameters);
     Optional<UserDto> otherUserToIndex = detectEmailUpdate(dbSession, authenticatorParameters, disabledUser != null ? disabledUser.getUuid() : null);
     NewUser newUser = createNewUser(authenticatorParameters);
     if (disabledUser == null) {
@@ -167,6 +172,17 @@ public class UserRegistrarImpl implements UserRegistrar {
     return userUpdater.reactivateAndCommit(dbSession, disabledUser, newUser, beforeCommit(dbSession, authenticatorParameters), toArray(otherUserToIndex));
   }
 
+  private void blockUnmanagedUserCreationOnManagedInstance(UserRegistration userRegistration) {
+    if (managedInstanceService.isInstanceExternallyManaged() && !userRegistration.managed()) {
+      throw AuthenticationException.newBuilder()
+        .setMessage("No account found for this user. As the instance is managed, make sure to provision the user from your IDP.")
+        .setPublicMessage("You have no account on SonarQube. Please make sure with your administrator that your account is provisioned.")
+        .setLogin(userRegistration.getUserIdentity().getProviderLogin())
+        .setSource(userRegistration.getSource())
+        .build();
+    }
+  }
+
   private UserDto registerExistingUser(DbSession dbSession, UserDto userDto, UserRegistration authenticatorParameters) {
     UpdateUser update = new UpdateUser()
       .setEmail(authenticatorParameters.getUserIdentity().getEmail())
index a3bbc60bf1617ad7098713e7fd2c27251c86c988..576125644ee2b48756ddac0af1b7d8068f4631ab 100644 (file)
@@ -19,9 +19,6 @@
  */
 package org.sonar.server.authentication;
 
-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.server.authentication.event.AuthenticationEvent;
@@ -33,11 +30,13 @@ public class UserRegistration {
   private final UserIdentity userIdentity;
   private final IdentityProvider provider;
   private final AuthenticationEvent.Source source;
+  private final boolean managed;
 
   UserRegistration(Builder builder) {
     this.userIdentity = builder.userIdentity;
     this.provider = builder.provider;
     this.source = builder.source;
+    this.managed = builder.managed;
   }
 
   public UserIdentity getUserIdentity() {
@@ -52,6 +51,10 @@ public class UserRegistration {
     return source;
   }
 
+  public boolean managed() {
+    return managed;
+  }
+
   public static UserRegistration.Builder builder() {
     return new Builder();
   }
@@ -60,6 +63,7 @@ public class UserRegistration {
     private UserIdentity userIdentity;
     private IdentityProvider provider;
     private AuthenticationEvent.Source source;
+    private boolean managed = false;
 
     public Builder setUserIdentity(UserIdentity userIdentity) {
       this.userIdentity = userIdentity;
@@ -76,6 +80,11 @@ public class UserRegistration {
       return this;
     }
 
+    public Builder setManaged(boolean managed) {
+      this.managed = managed;
+      return this;
+    }
+
     public UserRegistration build() {
       requireNonNull(userIdentity, "userIdentity must be set");
       requireNonNull(provider, "identityProvider must be set");
index 056092f1be32350dcad7ec106ae938dff6d37de5..4e8a3d82facd9376cc1d309722bebd3b1beea406 100644 (file)
@@ -41,6 +41,7 @@ import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.authentication.event.AuthenticationException;
 import org.sonar.server.es.EsTester;
+import org.sonar.server.management.ManagedInstanceService;
 import org.sonar.server.user.NewUserNotifier;
 import org.sonar.server.user.UserUpdater;
 import org.sonar.server.user.index.UserIndexer;
@@ -97,7 +98,7 @@ public class HttpHeadersAuthenticationTest {
   private final DefaultGroupFinder defaultGroupFinder = new DefaultGroupFinder(db.getDbClient());
   private final UserRegistrarImpl userIdentityAuthenticator = new UserRegistrarImpl(db.getDbClient(),
     new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, defaultGroupFinder, settings.asConfig(), mock(AuditPersister.class), localAuthentication),
-    defaultGroupFinder);
+    defaultGroupFinder, mock(ManagedInstanceService.class));
   private final HttpServletResponse response = mock(HttpServletResponse.class);
   private final JwtHttpHandler jwtHttpHandler = mock(JwtHttpHandler.class);
   private final AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
index 920f359599714d06837979a53efc9c30d9079d48..070f9a6b33088d1d701f8efe411d66d004119438 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.server.authentication.event.AuthenticationEvent;
 import org.sonar.server.authentication.event.AuthenticationEvent.Source;
 import org.sonar.server.authentication.event.AuthenticationException;
 import org.sonar.server.es.EsTester;
+import org.sonar.server.management.ManagedInstanceService;
 import org.sonar.server.user.NewUserNotifier;
 import org.sonar.server.user.UserUpdater;
 import org.sonar.server.user.index.UserIndexer;
@@ -51,6 +52,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.sonar.server.authentication.UserRegistrarImpl.GITHUB_PROVIDER;
 import static org.sonar.server.authentication.UserRegistrarImpl.GITLAB_PROVIDER;
 import static org.sonar.server.authentication.UserRegistrarImpl.LDAP_PROVIDER_PREFIX;
@@ -88,9 +90,10 @@ public class UserRegistrarImplTest {
   private final CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient(), settings.asConfig());
   private final DefaultGroupFinder groupFinder = new DefaultGroupFinder(db.getDbClient());
   private final AuditPersister auditPersister = mock(AuditPersister.class);
+  private final ManagedInstanceService managedInstanceService = mock(ManagedInstanceService.class);
   private final UserUpdater userUpdater = new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, groupFinder, settings.asConfig(), auditPersister, localAuthentication);
 
-  private final UserRegistrarImpl underTest = new UserRegistrarImpl(db.getDbClient(), userUpdater, groupFinder);
+  private final UserRegistrarImpl underTest = new UserRegistrarImpl(db.getDbClient(), userUpdater, groupFinder, managedInstanceService);
   private GroupDto defaultGroup;
 
   @Before
@@ -234,6 +237,37 @@ public class UserRegistrarImplTest {
       .hasFieldOrPropertyWithValue("publicMessage", "'github' users are not allowed to sign up");
   }
 
+  @Test
+  public void register_whenNewUnmanagedUserAndManagedInstance_shouldThrow() {
+    when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(true);
+
+    TestIdentityProvider identityProvider = composeIdentityProvider("saml", "Okta", true, true);
+    Source source = realm(AuthenticationEvent.Method.FORM, identityProvider.getName());
+    UserRegistration registration = composeUserRegistration(USER_IDENTITY, identityProvider, source);
+
+    assertThatThrownBy(() -> underTest.register(registration))
+      .isInstanceOf(AuthenticationException.class)
+      .hasMessage("No account found for this user. As the instance is managed, make sure to provision the user from your IDP.")
+      .hasFieldOrPropertyWithValue("source", source)
+      .hasFieldOrPropertyWithValue("login", USER_IDENTITY.getProviderLogin());
+  }
+
+  @Test
+  public void register_whenNewManagedUserAndManagedInstance_shouldCreateAndReturnUser() {
+    when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(true);
+
+    TestIdentityProvider identityProvider = composeIdentityProvider("saml", "Okta", true, true);
+    Source source = realm(AuthenticationEvent.Method.FORM, identityProvider.getName());
+    UserRegistration registration = composeUserRegistration(USER_IDENTITY, identityProvider, source, true);
+
+    UserDto userDto = underTest.register(registration);
+
+    assertThat(userDto.getExternalId()).isEqualTo(USER_IDENTITY.getProviderId());
+    assertThat(userDto.getExternalLogin()).isEqualTo(USER_IDENTITY.getProviderLogin());
+    assertThat(userDto.getName()).isEqualTo(USER_IDENTITY.getName());
+    assertThat(userDto.getEmail()).isEqualTo(USER_IDENTITY.getEmail());
+  }
+
   @Test
   public void authenticate_existing_user_doesnt_change_group_membership() {
     UserDto user = db.users().insertUser(u -> u.setExternalIdentityProvider(GH_IDENTITY_PROVIDER.getKey()));
@@ -594,12 +628,17 @@ public class UserRegistrarImplTest {
       .setAllowsUsersToSignUp(allowsUsersToSignUp);
   }
 
-  private static UserRegistration composeUserRegistration(UserIdentity userIdentity, IdentityProvider identityProvider, Source source) {
+  private static UserRegistration composeUserRegistration(UserIdentity userIdentity, IdentityProvider identityProvider, Source source, Boolean managed) {
     return UserRegistration.builder()
       .setUserIdentity(userIdentity)
       .setProvider(identityProvider)
       .setSource(source)
+      .setManaged(managed)
       .build();
   }
 
+  private static UserRegistration composeUserRegistration(UserIdentity userIdentity, IdentityProvider identityProvider, Source source) {
+    return composeUserRegistration(userIdentity, identityProvider, source, false);
+  }
+
 }