package org.sonar.server.user;
import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
if (scmAccount.equals(login) || scmAccount.equals(email)) {
messages.add(Message.of("user.login_or_email_used_as_scm_account"));
} else {
- UserDto matchingUser = dbClient.userDao().selectNullableByScmAccountOrLoginOrName(dbSession, scmAccount);
- if (matchingUser != null && (existingUser == null || !matchingUser.getId().equals(existingUser.getId()))) {
- messages.add(Message.of("user.scm_account_already_used", scmAccount, matchingUser.getName(), matchingUser.getLogin()));
+ List<UserDto> matchingUsers = dbClient.userDao().selectNullableByScmAccountOrLoginOrEmail(dbSession, scmAccount);
+ List<String> matchingUsersWithoutExistingUser = newArrayList();
+ for (UserDto matchingUser : matchingUsers) {
+ if (existingUser == null || !matchingUser.getId().equals(existingUser.getId())) {
+ matchingUsersWithoutExistingUser.add(matchingUser.getName() + " (" + matchingUser.getLogin() + ")");
+ }
+ }
+ if (!matchingUsersWithoutExistingUser.isEmpty()) {
+ messages.add(Message.of("user.scm_account_already_used", scmAccount, Joiner.on(", ").join(matchingUsersWithoutExistingUser)));
}
}
}
import javax.annotation.CheckForNull;
+import java.util.List;
+
public class UserDao extends org.sonar.core.user.UserDao implements DaoComponent {
public UserDao(MyBatis mybatis, System2 system2) {
return user;
}
- @CheckForNull
- public UserDto selectNullableByScmAccountOrLoginOrName(DbSession session, String scmAccount) {
- return mapper(session).selectNullableByScmAccountOrLoginOrName(scmAccount);
+ public List<UserDto> selectNullableByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) {
+ return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail);
}
protected UserMapper mapper(DbSession session) {
.should(QueryBuilders.termQuery(UserIndexDefinition.FIELD_LOGIN, scmAccount))
.should(QueryBuilders.termQuery(UserIndexDefinition.FIELD_EMAIL, scmAccount))
.should(QueryBuilders.termQuery(UserIndexDefinition.FIELD_SCM_ACCOUNTS, scmAccount)))
- .setSize(1);
+ .setSize(2);
SearchHit[] result = request.get().getHits().getHits();
if (result.length == 1) {
return new UserDoc(result[0].sourceAsMap());
try {
userUpdater.create(NewUser.create()
.setLogin("marius")
- .setName("Marius2")
- .setEmail("marius2@mail.com")
- .setPassword("password2")
- .setPasswordConfirmation("password2")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
.setScmAccounts(newArrayList("jo")));
fail();
} catch (BadRequestException e) {
- assertThat(e.errors().messages()).containsOnly(Message.of("user.scm_account_already_used", "jo", "John", "john"));
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.scm_account_already_used", "jo", "John (john)"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_when_scm_account_is_already_used_by_many_user() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml");
+
+ try {
+ userUpdater.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("john@email.com")));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.scm_account_already_used", "john@email.com", "John (john), Technical account (technical-account)"));
}
}
.setScmAccounts(newArrayList("jo")));
fail();
} catch (BadRequestException e) {
- assertThat(e.errors().messages()).containsOnly(Message.of("user.scm_account_already_used", "jo", "John", "john"));
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.scm_account_already_used", "jo", "John (john)"));
}
}
import org.sonar.core.user.UserDto;
import org.sonar.server.exceptions.NotFoundException;
+import java.util.List;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
public void select_nullable_by_scm_account() {
db.prepareDbUnit(getClass(), "select_nullable_by_scm_account.xml");
- assertThat(dao.selectNullableByScmAccountOrLoginOrName(session, "ma").getLogin()).isEqualTo("marius");
- assertThat(dao.selectNullableByScmAccountOrLoginOrName(session, "marius").getLogin()).isEqualTo("marius");
- assertThat(dao.selectNullableByScmAccountOrLoginOrName(session, "marius@lesbronzes.fr").getLogin()).isEqualTo("marius");
+ List<UserDto> results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "ma");
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getLogin()).isEqualTo("marius");
+
+ results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "marius");
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getLogin()).isEqualTo("marius");
+
+ results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "marius@lesbronzes.fr");
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getLogin()).isEqualTo("marius");
+
+ results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "marius@lesbronzes.fr");
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getLogin()).isEqualTo("marius");
+
+ results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "m");
+ assertThat(results).isEmpty();
+
+ results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "unknown");
+ assertThat(results).isEmpty();
+ }
+
+ @Test
+ public void select_nullable_by_scm_account_return_many_results_when_same_email_is_used_by_many_users() {
+ db.prepareDbUnit(getClass(), "select_nullable_by_scm_account_return_many_results_when_same_email_is_used_by_many_users.xml");
- assertThat(dao.selectNullableByScmAccountOrLoginOrName(session, "m")).isNull();
- assertThat(dao.selectNullableByScmAccountOrLoginOrName(session, "unknown")).isNull();
+ List<UserDto> results = dao.selectNullableByScmAccountOrLoginOrEmail(session, "marius@lesbronzes.fr");
+ assertThat(results).hasSize(2);
}
@Test
assertThat(index.getNullableByScmAccount("unknown")).isNull();
}
+ @Test
+ public void get_nullable_by_scm_account_return_null_when_two_users_have_same_email() throws Exception {
+ esTester.putDocuments(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, this.getClass(), "user1.json", "user3-with-same-email-as-user1.json");
+
+ assertThat(index.getNullableByScmAccount("user1@mail.com")).isNull();
+ }
+
}
--- /dev/null
+<dataset>
+
+ <users id="101" login="john" name="John" email="john@email.com" active="[true]" scm_accounts=",jo," created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+ <users id="102" login="technical-account" name="Technical account" email="john@email.com" active="[true]" scm_accounts="[null]" created_at="1418215735482"
+ updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts=",ma,marius33," created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+ <users id="102" login="sbrandhof" name="Simon Brandhof" email="marius@lesbronzes.fr" active="[true]" scm_accounts="[null]" created_at="1418215735482"
+ updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8366" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fh"/>
+
+</dataset>
--- /dev/null
+{
+ "login": "user3",
+ "name": "User3",
+ "email": "user1@mail.com",
+ "active": true,
+ "scmAccounts": ["u3"],
+ "createdAt": 1500000000000,
+ "updatedAt": 1500000000000
+}
+
@CheckForNull
UserDto selectByLogin(String login);
+ /**
+ * Search for a user by SCM account, login or email.
+ * Can return multiple results if an email is used by many users (For instance, technical account can use the same email as a none technical account)
+ */
@CheckForNull
- UserDto selectNullableByScmAccountOrLoginOrName(String scmAccount);
+ List<UserDto> selectNullableByScmAccountOrLoginOrEmail(String scmAccountOrLoginOrEmail);
@CheckForNull
UserDto selectUser(long userId);
WHERE u.login=#{login}
</select>
- <select id="selectNullableByScmAccountOrLoginOrName" parameterType="String" resultType="User">
+ <select id="selectNullableByScmAccountOrLoginOrEmail" parameterType="String" resultType="User">
SELECT
<include refid="userColumns"/>
FROM users u
user.password_doesnt_match_confirmation=Password doesn't match confirmation.
user.reactivated=The user '{0}' has been reactivated.
user.add_scm_account=Add SCM account
-user.scm_account_already_used=The scm account '{0}' is already used by user '{1} ({2})'
+user.scm_account_already_used=The scm account '{0}' is already used by user(s) : '{1}'
user.login_or_email_used_as_scm_account=Login and email are automatically considered as SCM accounts