]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10597 Update USER_TOKENS#LOGIN to USER_UUID
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 11 May 2018 07:20:20 +0000 (09:20 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 23 May 2018 18:20:47 +0000 (20:20 +0200)
* SONAR-10597 Update USER_TOKENS#LOGIN to USER_UUD in DB

* SONAR-10597 Update UserTokenDao to return user uuid

* SONAR-10597 Fix api/user_tokens/generate to correctly use user uuid

* SONAR-10597 Fix api/user_tokens/revoke to correctly use user uuid

* SONAR-10597 Fix authentication by token to correctly use user uuid

* SONAR-10597 Fix api/user_tokens/search to correctly use user uuid

* SONAR-10597 Fix api/users/search to correctly use user uuid

* SONAR-10597 Add ITs to check user tokens after login update

38 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenCount.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserTokenMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserTokenDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserTokenTesting.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokens.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest/user_tokens.sql [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/authentication/BasicAuthenticator.java
server/sonar-server/src/main/java/org/sonar/server/user/ws/DeactivateAction.java
server/sonar-server/src/main/java/org/sonar/server/user/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenAuthenticator.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/UserTokenModule.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/RevokeAction.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokenSupport.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokensWs.java
server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokensWsParameters.java
server/sonar-server/src/test/java/org/sonar/server/authentication/BasicAuthenticatorTest.java
server/sonar-server/src/test/java/org/sonar/server/user/ws/DeactivateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/user/ws/SearchActionTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticatorTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/UserTokenModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/RevokeActionTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/SearchActionTest.java
server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java
tests/src/test/java/org/sonarqube/tests/user/SonarCloudUpdateLoginDuringAuthenticationTest.java
tests/src/test/java/org/sonarqube/tests/user/UserSuite.java
tests/src/test/java/org/sonarqube/tests/user/UserTokensTest.java [new file with mode: 0644]

index 885d377e2c0bbb65b7a4459355cb42c863798fdd..3c5de10a498184fa2a83bed52e1afcf0c1b7ef27 100644 (file)
@@ -701,13 +701,13 @@ CREATE TABLE "CE_SCANNER_CONTEXT" (
 
 CREATE TABLE "USER_TOKENS" (
   "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
-  "LOGIN" VARCHAR(255) NOT NULL,
+  "USER_UUID" VARCHAR(255) NOT NULL,
   "NAME" VARCHAR(100) NOT NULL,
   "TOKEN_HASH" VARCHAR(255) NOT NULL,
   "CREATED_AT" BIGINT NOT NULL
 );
 CREATE UNIQUE INDEX "USER_TOKENS_TOKEN_HASH" ON "USER_TOKENS" ("TOKEN_HASH");
-CREATE UNIQUE INDEX "USER_TOKENS_LOGIN_NAME" ON "USER_TOKENS" ("LOGIN", "NAME");
+CREATE UNIQUE INDEX "USER_TOKENS_USER_UUID_NAME" ON "USER_TOKENS" ("USER_UUID", "NAME");
 
 
 CREATE TABLE "ES_QUEUE" (
index 6bcbc2254a504a6e83da3ec77dc7f25f14766d4e..9617816fda8260a6178ba2f226dba62a44eb1d05 100644 (file)
 package org.sonar.db.user;
 
 public class UserTokenCount {
-  private String login;
+  private String userUuid;
   private Integer tokenCount;
 
-  public String getLogin() {
-    return login;
+  public String getUserUuid() {
+    return userUuid;
   }
 
-  public UserTokenCount setLogin(String login) {
-    this.login = login;
+  public UserTokenCount setUserUuid(String userUuid) {
+    this.userUuid = userUuid;
     return this;
   }
 
index 5a3b8c0054f478a835646d92e55a4efb11ce17ab..8391010fe3abf88d66d7fe39dbd5aed43904bf61 100644 (file)
  */
 package org.sonar.db.user;
 
-import com.google.common.base.Optional;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.CheckForNull;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
-import org.sonar.db.RowNotFoundException;
 
-import static java.lang.String.format;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 
 public class UserTokenDao implements Dao {
@@ -35,35 +35,28 @@ public class UserTokenDao implements Dao {
     mapper(dbSession).insert(userTokenDto);
   }
 
-  public UserTokenDto selectOrFailByTokenHash(DbSession dbSession, String tokenHash) {
-    UserTokenDto userToken = mapper(dbSession).selectByTokenHash(tokenHash);
-    if (userToken == null) {
-      throw new RowNotFoundException(format("User token with token hash '%s' not found", tokenHash));
-    }
-
-    return userToken;
-  }
-
-  public Optional<UserTokenDto> selectByTokenHash(DbSession dbSession, String tokenHash) {
-    return Optional.fromNullable(mapper(dbSession).selectByTokenHash(tokenHash));
+  @CheckForNull
+  public UserTokenDto selectByTokenHash(DbSession dbSession, String tokenHash) {
+    return mapper(dbSession).selectByTokenHash(tokenHash);
   }
 
-  public Optional<UserTokenDto> selectByLoginAndName(DbSession dbSession, String login, String name) {
-    return Optional.fromNullable(mapper(dbSession).selectByLoginAndName(login, name));
+  @CheckForNull
+  public UserTokenDto selectByUserAndName(DbSession dbSession, UserDto user, String name) {
+    return mapper(dbSession).selectByUserUuidAndName(user.getUuid(), name);
   }
 
-  public List<UserTokenDto> selectByLogin(DbSession dbSession, String login) {
-    return mapper(dbSession).selectByLogin(login);
+  public List<UserTokenDto> selectByUser(DbSession dbSession, UserDto user) {
+    return mapper(dbSession).selectByUserUuid(user.getUuid());
   }
 
-  public Map<String, Integer> countTokensByLogins(DbSession dbSession, List<String> logins) {
-    Map<String, Integer> result = new HashMap<>(logins.size());
+  public Map<String, Integer> countTokensByUsers(DbSession dbSession, Collection<UserDto> users) {
+    Map<String, Integer> result = new HashMap<>(users.size());
     executeLargeInputs(
-      logins,
+      users.stream().map(UserDto::getUuid).collect(toList()),
       input -> {
-        List<UserTokenCount> userTokenCounts = mapper(dbSession).countTokensByLogins(input);
+        List<UserTokenCount> userTokenCounts = mapper(dbSession).countTokensByUserUuids(input);
         for (UserTokenCount userTokenCount : userTokenCounts) {
-          result.put(userTokenCount.getLogin(), userTokenCount.tokenCount());
+          result.put(userTokenCount.getUserUuid(), userTokenCount.tokenCount());
         }
         return userTokenCounts;
       });
@@ -71,12 +64,12 @@ public class UserTokenDao implements Dao {
     return result;
   }
 
-  public void deleteByLogin(DbSession dbSession, String login) {
-    mapper(dbSession).deleteByLogin(login);
+  public void deleteByUser(DbSession dbSession, UserDto user) {
+    mapper(dbSession).deleteByUserUuid(user.getUuid());
   }
 
-  public void deleteByLoginAndName(DbSession dbSession, String login, String name) {
-    mapper(dbSession).deleteByLoginAndName(login, name);
+  public void deleteByUserAndName(DbSession dbSession, UserDto user, String name) {
+    mapper(dbSession).deleteByUserUuidAndName(user.getUuid(), name);
   }
 
   private static UserTokenMapper mapper(DbSession dbSession) {
index d700c5e1cfc3c4c690eda9e9cad288949824d39a..c014283f2b1b2d36db746486e8119edad9bd2155 100644 (file)
@@ -22,17 +22,17 @@ package org.sonar.db.user;
 import static org.sonar.db.user.UserTokenValidator.checkTokenHash;
 
 public class UserTokenDto {
-  private String login;
+  private String userUuid;
   private String name;
   private String tokenHash;
   private Long createdAt;
 
-  public String getLogin() {
-    return login;
+  public String getUserUuid() {
+    return userUuid;
   }
 
-  public UserTokenDto setLogin(String login) {
-    this.login = login;
+  public UserTokenDto setUserUuid(String userUuid) {
+    this.userUuid = userUuid;
     return this;
   }
 
index c06e4738cb97e509dbaeb13aeb889f06d8b95726..82a0ac7d7d618053f1e405cb329a933e7b50d7d4 100644 (file)
@@ -27,13 +27,13 @@ public interface UserTokenMapper {
 
   UserTokenDto selectByTokenHash(String tokenHash);
 
-  UserTokenDto selectByLoginAndName(@Param("login") String login, @Param("name") String name);
+  UserTokenDto selectByUserUuidAndName(@Param("userUuid") String userUuid, @Param("name") String name);
 
-  List<UserTokenDto> selectByLogin(String login);
+  List<UserTokenDto> selectByUserUuid(String userUuid);
 
-  void deleteByLogin(String login);
+  void deleteByUserUuid(String userUuid);
 
-  void deleteByLoginAndName(@Param("login") String login, @Param("name") String name);
+  void deleteByUserUuidAndName(@Param("userUuid") String userUuid, @Param("name") String name);
 
-  List<UserTokenCount> countTokensByLogins(@Param("logins") List<String> logins);
+  List<UserTokenCount> countTokensByUserUuids(@Param("userUuids") List<String> userUuids);
 }
index 5ffa6465199f1992a3122179cc96006063264175..085e5cbc4484ab10624cea347fec2a089d047aba 100644 (file)
@@ -4,7 +4,7 @@
 <mapper namespace="org.sonar.db.user.UserTokenMapper">
 
   <sql id="userTokensColumns">
-    t.login as "login",
+    t.user_uuid as "userUuid",
     t.name as "name",
     t.token_hash as "tokenHash",
     t.created_at as "createdAt"
 
   <insert id="insert" parameterType="UserToken" keyColumn="id" useGeneratedKeys="false" keyProperty="id">
     insert into user_tokens (
-    login,
+    user_uuid,
     name,
     token_hash,
     created_at
     ) values (
-    #{login,jdbcType=VARCHAR},
+    #{userUuid,jdbcType=VARCHAR},
     #{name,jdbcType=VARCHAR},
     #{tokenHash,jdbcType=VARCHAR},
     #{createdAt,jdbcType=BIGINT}
     WHERE t.token_hash=#{tokenHash}
   </select>
 
-  <select id="selectByLoginAndName" parameterType="map" resultType="UserToken">
+  <select id="selectByUserUuidAndName" parameterType="map" resultType="UserToken">
     SELECT
     <include refid="userTokensColumns"/>
     FROM user_tokens t
-    WHERE t.login=#{login} and t.name=#{name}
+    WHERE t.user_uuid=#{userUuid} and t.name=#{name}
   </select>
 
-  <select id="selectByLogin" parameterType="map" resultType="UserToken">
+  <select id="selectByUserUuid" parameterType="map" resultType="UserToken">
     SELECT
     <include refid="userTokensColumns"/>
     FROM user_tokens t
-    WHERE t.login=#{login}
+    WHERE t.user_uuid=#{userUuid}
   </select>
   
-  <select id="countTokensByLogins" parameterType="map" resultType="UserTokenCount">
-    SELECT t.login as "login", count(t.name) as "tokenCount"
+  <select id="countTokensByUserUuids" parameterType="map" resultType="UserTokenCount">
+    SELECT t.user_uuid as "userUuid", count(t.name) as "tokenCount"
     FROM user_tokens t
-    WHERE t.login in
-    <foreach collection="logins" open="(" close=")" item="login" separator=",">
-      #{login}
+    WHERE t.user_uuid in
+    <foreach collection="userUuids" open="(" close=")" item="userUuid" separator=",">
+      #{userUuid}
     </foreach>
-    GROUP BY t.login
+    GROUP BY t.user_uuid
   </select>
 
-  <delete id="deleteByLogin">
-    DELETE FROM user_tokens WHERE login=#{login}
+  <delete id="deleteByUserUuid">
+    DELETE FROM user_tokens WHERE user_uuid=#{userUuid}
   </delete>
 
-  <delete id="deleteByLoginAndName">
-    DELETE FROM user_tokens WHERE login=#{login} and name=#{name}
+  <delete id="deleteByUserUuidAndName">
+    DELETE FROM user_tokens WHERE user_uuid=#{userUuid} and name=#{name}
   </delete>
 
 </mapper>
index 69343f0b9cc30ee7f0fbc9fd98c28675ab4d848a..1a9ecabfd31400c7d4627b035af3ff091ece2d23 100644 (file)
@@ -42,6 +42,7 @@ import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.db.user.GroupTesting.newGroupDto;
 import static org.sonar.db.user.UserTesting.newDisabledUser;
 import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.db.user.UserTokenTesting.newUserToken;
 
 public class UserDbTester {
   private final DbTester db;
@@ -227,7 +228,7 @@ public class UserDbTester {
     checkArgument(!project.isPrivate(), "No permission to group AnyOne can be granted on a private project");
     checkArgument(!ProjectPermissions.PUBLIC_PERMISSIONS.contains(permission),
       "permission %s can't be granted on a public project", permission);
-    checkArgument(project.getMainBranchProjectUuid()==null, "Permissions can't be granted on branches");
+    checkArgument(project.getMainBranchProjectUuid() == null, "Permissions can't be granted on branches");
     GroupPermissionDto dto = new GroupPermissionDto()
       .setOrganizationUuid(project.getOrganizationUuid())
       .setGroupId(null)
@@ -247,7 +248,7 @@ public class UserDbTester {
     checkArgument(group.getOrganizationUuid().equals(project.getOrganizationUuid()), "Different organizations");
     checkArgument(project.isPrivate() || !ProjectPermissions.PUBLIC_PERMISSIONS.contains(permission),
       "%s can't be granted on a public project", permission);
-    checkArgument(project.getMainBranchProjectUuid()==null, "Permissions can't be granted on branches");
+    checkArgument(project.getMainBranchProjectUuid() == null, "Permissions can't be granted on branches");
     GroupPermissionDto dto = new GroupPermissionDto()
       .setOrganizationUuid(group.getOrganizationUuid())
       .setGroupId(group.getId())
@@ -320,7 +321,7 @@ public class UserDbTester {
   public UserPermissionDto insertProjectPermissionOnUser(UserDto user, String permission, ComponentDto project) {
     checkArgument(project.isPrivate() || !ProjectPermissions.PUBLIC_PERMISSIONS.contains(permission),
       "%s can't be granted on a public project", permission);
-    checkArgument(project.getMainBranchProjectUuid()==null, "Permissions can't be granted on branches");
+    checkArgument(project.getMainBranchProjectUuid() == null, "Permissions can't be granted on branches");
     UserPermissionDto dto = new UserPermissionDto(project.getOrganizationUuid(), permission, user.getId(), project.getId());
     db.getDbClient().userPermissionDao().insert(db.getSession(), dto);
     db.commit();
@@ -342,4 +343,14 @@ public class UserDbTester {
       .map(OrganizationPermission::fromKey)
       .collect(MoreCollectors.toList());
   }
+
+  @SafeVarargs
+  public final UserTokenDto insertToken(UserDto user, Consumer<UserTokenDto>... populators) {
+    UserTokenDto dto = newUserToken().setUserUuid(user.getUuid());
+    stream(populators).forEach(p -> p.accept(dto));
+    db.getDbClient().userTokenDao().insert(db.getSession(), dto);
+    db.commit();
+    return dto;
+  }
+
 }
index d6d9a9a6c1f77a18e011b5ee91f25c6807184052..6807d847e9e4776d1aafef9d76b272afdce1232a 100644 (file)
  */
 package org.sonar.db.user;
 
-import com.google.common.base.Optional;
 import java.util.Map;
-import org.assertj.guava.api.Assertions;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.RowNotFoundException;
 
-import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.db.user.UserTokenTesting.newUserToken;
 
-
 public class UserTokenDaoTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  DbSession dbSession;
-
-  UserTokenDao underTest;
+  private DbSession dbSession = db.getSession();
 
-  @Before
-  public void setUp() {
-    underTest = db.getDbClient().userTokenDao();
-    dbSession = db.getSession();
-  }
+  private UserTokenDao underTest = db.getDbClient().userTokenDao();
 
   @Test
   public void insert_token() {
     UserTokenDto userToken = newUserToken();
 
-    insertToken(userToken);
+    underTest.insert(db.getSession(), userToken);
 
-    UserTokenDto userTokenFromDb = underTest.selectOrFailByTokenHash(dbSession, userToken.getTokenHash());
+    UserTokenDto userTokenFromDb = underTest.selectByTokenHash(db.getSession(), userToken.getTokenHash());
     assertThat(userTokenFromDb).isNotNull();
     assertThat(userTokenFromDb.getName()).isEqualTo(userToken.getName());
     assertThat(userTokenFromDb.getCreatedAt()).isEqualTo(userToken.getCreatedAt());
     assertThat(userTokenFromDb.getTokenHash()).isEqualTo(userToken.getTokenHash());
-    assertThat(userTokenFromDb.getLogin()).isEqualTo(userToken.getLogin());
+    assertThat(userTokenFromDb.getUserUuid()).isEqualTo(userToken.getUserUuid());
   }
 
   @Test
   public void select_by_token_hash() {
+    UserDto user = db.users().insertUser();
     String tokenHash = "123456789";
-    insertToken(newUserToken().setTokenHash(tokenHash));
+    db.users().insertToken(user, t -> t.setTokenHash(tokenHash));
 
-    Optional<UserTokenDto> result = underTest.selectByTokenHash(dbSession, tokenHash);
+    UserTokenDto result = underTest.selectByTokenHash(db.getSession(), tokenHash);
 
-    Assertions.assertThat(result).isPresent();
+    assertThat(result).isNotNull();
   }
 
   @Test
-  public void fail_if_token_is_not_found() {
-    expectedException.expect(RowNotFoundException.class);
-    expectedException.expectMessage("User token with token hash 'unknown-token-hash' not found");
+  public void select_by_user_and_name() {
+    UserDto user = db.users().insertUser();
+    UserTokenDto userToken = db.users().insertToken(user, t -> t.setName("name").setTokenHash("token"));
 
-    underTest.selectOrFailByTokenHash(dbSession, "unknown-token-hash");
-  }
-
-  @Test
-  public void select_by_login_and_name() {
-    UserTokenDto userToken = newUserToken().setLogin("login").setName("name").setTokenHash("token");
-    insertToken(userToken);
-
-    Optional<UserTokenDto> optionalResultByLoginAndName = underTest.selectByLoginAndName(dbSession, userToken.getLogin(), userToken.getName());
-    UserTokenDto resultByLoginAndName = optionalResultByLoginAndName.get();
-    Optional<UserTokenDto> unfoundResult1 = underTest.selectByLoginAndName(dbSession, "unknown-login", userToken.getName());
-    Optional<UserTokenDto> unfoundResult2 = underTest.selectByLoginAndName(dbSession, userToken.getLogin(), "unknown-name");
-
-    Assertions.assertThat(unfoundResult1).isAbsent();
-    Assertions.assertThat(unfoundResult2).isAbsent();
-    assertThat(resultByLoginAndName.getLogin()).isEqualTo(userToken.getLogin());
+    UserTokenDto resultByLoginAndName = underTest.selectByUserAndName(db.getSession(), user, userToken.getName());
+    assertThat(resultByLoginAndName.getUserUuid()).isEqualTo(user.getUuid());
     assertThat(resultByLoginAndName.getName()).isEqualTo(userToken.getName());
     assertThat(resultByLoginAndName.getCreatedAt()).isEqualTo(userToken.getCreatedAt());
     assertThat(resultByLoginAndName.getTokenHash()).isEqualTo(userToken.getTokenHash());
+
+    assertThat(underTest.selectByUserAndName(db.getSession(), user, "unknown-name")).isNull();
   }
 
   @Test
-  public void delete_tokens_by_login() {
-    insertToken(newUserToken().setLogin("login-to-delete"));
-    insertToken(newUserToken().setLogin("login-to-delete"));
-    insertToken(newUserToken().setLogin("login-to-keep"));
-
-    underTest.deleteByLogin(dbSession, "login-to-delete");
+  public void delete_tokens_by_user() {
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    db.users().insertToken(user1);
+    db.users().insertToken(user1);
+    db.users().insertToken(user2);
+
+    underTest.deleteByUser(dbSession, user1);
     db.commit();
 
-    assertThat(underTest.selectByLogin(dbSession, "login-to-delete")).isEmpty();
-    assertThat(underTest.selectByLogin(dbSession, "login-to-keep")).hasSize(1);
+    assertThat(underTest.selectByUser(dbSession, user1)).isEmpty();
+    assertThat(underTest.selectByUser(dbSession, user2)).hasSize(1);
   }
 
   @Test
-  public void delete_token_by_login_and_name() {
-    insertToken(newUserToken().setLogin("login").setName("name"));
-    insertToken(newUserToken().setLogin("login").setName("another-name"));
-    insertToken(newUserToken().setLogin("another-login").setName("name"));
-
-    underTest.deleteByLoginAndName(dbSession, "login", "name");
-    db.commit();
-
-    Assertions.assertThat(underTest.selectByLoginAndName(dbSession, "login", "name")).isAbsent();
-    Assertions.assertThat(underTest.selectByLoginAndName(dbSession, "login", "another-name")).isPresent();
-    Assertions.assertThat(underTest.selectByLoginAndName(dbSession, "another-login", "name")).isPresent();
+  public void delete_token_by_user_and_name() {
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    db.users().insertToken(user1, t -> t.setName("name"));
+    db.users().insertToken(user1, t -> t.setName("another-name"));
+    db.users().insertToken(user2, t -> t.setName("name"));
+
+    underTest.deleteByUserAndName(dbSession, user1, "name");
+
+    assertThat(underTest.selectByUserAndName(dbSession, user1, "name")).isNull();
+    assertThat(underTest.selectByUserAndName(dbSession, user1, "another-name")).isNotNull();
+    assertThat(underTest.selectByUserAndName(dbSession, user2, "name")).isNotNull();
   }
 
   @Test
-  public void count_tokens_by_login() {
-    insertToken(newUserToken().setLogin("login").setName("name"));
-    insertToken(newUserToken().setLogin("login").setName("another-name"));
-
-    Map<String, Integer> result = underTest.countTokensByLogins(dbSession, newArrayList("login"));
+  public void count_tokens_by_user() {
+    UserDto user = db.users().insertUser();
+    db.users().insertToken(user, t -> t.setName("name"));
+    db.users().insertToken(user, t -> t.setName("another-name"));
 
-    assertThat(result.get("login")).isEqualTo(2);
-    assertThat(result.get("unknown-login")).isNull();
-  }
+    Map<String, Integer> result = underTest.countTokensByUsers(dbSession, singletonList(user));
 
-  private void insertToken(UserTokenDto userToken) {
-    underTest.insert(dbSession, userToken);
-    dbSession.commit();
+    assertThat(result.get(user.getUuid())).isEqualTo(2);
+    assertThat(result.get("unknown-user_uuid")).isNull();
   }
 }
index b398331254638870e2e0ba99e902f5054c1677e3..3a2a5916add2eae16f676a78d54313004e55f6ac 100644 (file)
@@ -25,9 +25,9 @@ import static org.apache.commons.lang.math.RandomUtils.nextLong;
 public class UserTokenTesting {
   public static UserTokenDto newUserToken() {
     return new UserTokenDto()
-      .setLogin(randomAlphanumeric(255))
-      .setName(randomAlphanumeric(100))
-      .setTokenHash(randomAlphanumeric(40))
+      .setUserUuid("userUuid_" + randomAlphanumeric(40))
+      .setName("name_" + randomAlphanumeric(20))
+      .setTokenHash("hash_" + randomAlphanumeric(30))
       .setCreatedAt(nextLong());
   }
 }
index 4a5d0e223e3fa1ce39bd1a79c489b184fb6e5b38..c8cf8a2fbb1375cf4c0ec0af54f68bd7e010d4ef 100644 (file)
@@ -46,6 +46,7 @@ public class DbVersion72 implements DbVersion {
       .add(2116, "Populate ORGANIZATION_UUID in table users", PopulateOrganizationUuidOnUsers.class)
       .add(2117, "Drop USER_ID from table organizations", DropUserIdFromOrganizations.class)
       .add(2118, "Rename USER_LOGIN TO USER_UUID on table QPROFILE_CHANGES", RenameUserLoginToUserUuidOnTableQProfileChanges.class)
+      .add(2119, "Rename LOGIN TO USER_UUID on table USER_TOKENS", RenameLoginToUserUuidOnTableUserTokens.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokens.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokens.java
new file mode 100644 (file)
index 0000000..f68c8d3
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.platform.db.migration.version.v72;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.RenameColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class RenameLoginToUserUuidOnTableUserTokens extends DdlChange {
+
+  private static final String USER_TOKENS_TABLE = "user_tokens";
+
+  public RenameLoginToUserUuidOnTableUserTokens(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new DropIndexBuilder(getDialect())
+      .setTable(USER_TOKENS_TABLE)
+      .setName("user_tokens_login_name")
+      .build());
+
+    VarcharColumnDef userUuidColumn = newVarcharColumnDefBuilder()
+      .setColumnName("user_uuid")
+      .setLimit(255)
+      .setIsNullable(false)
+      .build();
+    context.execute(new RenameColumnsBuilder(getDialect(), USER_TOKENS_TABLE)
+      .renameColumn("login",
+        userUuidColumn)
+      .build());
+
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable(USER_TOKENS_TABLE)
+      .setName("user_tokens_user_uuid_name")
+      .setUnique(true)
+      .addColumn(userUuidColumn)
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("name")
+        .setLimit(100)
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+}
index 346d97cbcb65eadce7264043380df14d7e480c1e..4cd2886c5681839362d9f5fc6d9cffa3a6586bfa 100644 (file)
@@ -34,7 +34,7 @@ public class DbVersion72Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 19);
+    verifyMigrationCount(underTest, 20);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest.java
new file mode 100644 (file)
index 0000000..656863f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.platform.db.migration.version.v72;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class RenameLoginToUserUuidOnTableUserTokensTest {
+
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(RenameLoginToUserUuidOnTableUserTokensTest.class, "user_tokens.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private RenameLoginToUserUuidOnTableUserTokens underTest = new RenameLoginToUserUuidOnTableUserTokens(db.database());
+
+  @Test
+  public void rename_column() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDefinition("user_tokens", "user_uuid", VARCHAR, 255, false);
+    db.assertColumnDoesNotExist("user_tokens", "login");
+    db.assertUniqueIndex("user_tokens", "user_tokens_user_uuid_name", "user_uuid", "name");
+    db.assertIndexDoesNotExist("user_tokens", "user_tokens_login_name");
+  }
+
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest/user_tokens.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v72/RenameLoginToUserUuidOnTableUserTokensTest/user_tokens.sql
new file mode 100644 (file)
index 0000000..acb1b33
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE TABLE "USER_TOKENS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "LOGIN" VARCHAR(255) NOT NULL,
+  "NAME" VARCHAR(100) NOT NULL,
+  "TOKEN_HASH" VARCHAR(255) NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "USER_TOKENS_TOKEN_HASH" ON "USER_TOKENS" ("TOKEN_HASH");
+CREATE UNIQUE INDEX "USER_TOKENS_LOGIN_NAME" ON "USER_TOKENS" ("LOGIN", "NAME");
\ No newline at end of file
index a8551096123240ab22831b16f5dcee1e9e0552bb..d8d8f8b082b4bbfec71b5922dcc38a241ddaee13 100644 (file)
@@ -106,16 +106,16 @@ public class BasicAuthenticator {
   }
 
   private UserDto authenticateFromUserToken(String token) {
-    Optional<String> authenticatedLogin = userTokenAuthenticator.authenticate(token);
-    if (!authenticatedLogin.isPresent()) {
+    Optional<String> authenticatedUserUuid = userTokenAuthenticator.authenticate(token);
+    if (!authenticatedUserUuid.isPresent()) {
       throw AuthenticationException.newBuilder()
         .setSource(Source.local(Method.BASIC_TOKEN))
         .setMessage("Token doesn't exist")
         .build();
     }
     try (DbSession dbSession = dbClient.openSession(false)) {
-      UserDto userDto = dbClient.userDao().selectActiveUserByLogin(dbSession, authenticatedLogin.get());
-      if (userDto == null) {
+      UserDto userDto = dbClient.userDao().selectByUuid(dbSession, authenticatedUserUuid.get());
+      if (userDto == null || !userDto.isActive()) {
         throw AuthenticationException.newBuilder()
           .setSource(Source.local(Method.BASIC_TOKEN))
           .setMessage("User doesn't exist")
index fc8f3956ebeacfdd8e64d9196349e00296eb4066..e177bc1f86c88246cbbc79d01f2528cefb505a39 100644 (file)
@@ -94,7 +94,7 @@ public class DeactivateAction implements UsersWsAction {
       ensureNotLastAdministrator(dbSession, user);
 
       Integer userId = user.getId();
-      dbClient.userTokenDao().deleteByLogin(dbSession, login);
+      dbClient.userTokenDao().deleteByUser(dbSession, user);
       dbClient.propertiesDao().deleteByKeyAndValue(dbSession, DEFAULT_ISSUE_ASSIGNEE, user.getLogin());
       dbClient.propertiesDao().deleteByQuery(dbSession, PropertyQuery.builder().setUserId(userId).build());
       dbClient.userGroupDao().deleteByUserId(dbSession, userId);
index 961b482565009a5ca934704478ca3329d8c67996..0c3179718c02c3e22cfbd7f654c1ecbc34508f46 100644 (file)
@@ -123,8 +123,8 @@ public class SearchAction implements UsersWsAction {
     try (DbSession dbSession = dbClient.openSession(false)) {
       List<String> logins = result.getDocs().stream().map(UserDoc::login).collect(toList());
       Multimap<String, String> groupsByLogin = dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, logins);
-      Map<String, Integer> tokenCountsByLogin = dbClient.userTokenDao().countTokensByLogins(dbSession, logins);
       List<UserDto> users = dbClient.userDao().selectByOrderedLogins(dbSession, logins);
+      Map<String, Integer> tokenCountsByLogin = dbClient.userTokenDao().countTokensByUsers(dbSession, users);
       Paging paging = forPageIndex(request.getPage()).withPageSize(request.getPageSize()).andTotal((int) result.getTotal());
       return buildResponse(users, groupsByLogin, tokenCountsByLogin, fields, paging);
     }
@@ -133,7 +133,7 @@ public class SearchAction implements UsersWsAction {
   private SearchWsResponse buildResponse(List<UserDto> users, Multimap<String, String> groupsByLogin, Map<String, Integer> tokenCountsByLogin,
     @Nullable List<String> fields, Paging paging) {
     SearchWsResponse.Builder responseBuilder = newBuilder();
-    users.forEach(user -> responseBuilder.addUsers(towsUser(user, firstNonNull(tokenCountsByLogin.get(user.getLogin()), 0), groupsByLogin.get(user.getLogin()), fields)));
+    users.forEach(user -> responseBuilder.addUsers(towsUser(user, firstNonNull(tokenCountsByLogin.get(user.getUuid()), 0), groupsByLogin.get(user.getLogin()), fields)));
     responseBuilder.getPagingBuilder()
       .setPageIndex(paging.pageIndex())
       .setPageSize(paging.pageSize())
index 6387a20402363ae45ba811eb8f583c2148fdf3de..f6c4f2e2e0b6f612d03b822517a4eee3a469a174 100644 (file)
  */
 package org.sonar.server.usertoken;
 
-import com.google.common.base.Optional;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.user.UserTokenDto;
 
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+
 public class UserTokenAuthenticator {
   private final TokenGenerator tokenGenerator;
   private final DbClient dbClient;
@@ -34,18 +36,18 @@ public class UserTokenAuthenticator {
   }
 
   /**
-   * Returns the user login if the token hash is found, else {@code Optional.absent()}.
-   * The returned login is not validated. If database is corrupted (table USER_TOKENS badly purged
-   * for instance), then the login may not relate to a valid user.
+   * Returns the user uuid if the token hash is found, else {@code Optional.absent()}.
+   * The returned uuid is not validated. If database is corrupted (table USER_TOKENS badly purged
+   * for instance), then the uuid may not relate to a valid user.
    */
   public java.util.Optional<String> authenticate(String token) {
     String tokenHash = tokenGenerator.hash(token);
     try (DbSession dbSession = dbClient.openSession(false)) {
-      Optional<UserTokenDto> userToken = dbClient.userTokenDao().selectByTokenHash(dbSession, tokenHash);
-      if (userToken.isPresent()) {
-        return java.util.Optional.of(userToken.get().getLogin());
+      UserTokenDto userToken = dbClient.userTokenDao().selectByTokenHash(dbSession, tokenHash);
+      if (userToken == null) {
+        return empty();
       }
-      return java.util.Optional.empty();
+      return of(userToken.getUserUuid());
     }
   }
 }
index ab03cf56df417c044d1ea8b1802d79e99fb6bff8..cd1cfe07723268054994cea288b0add2f9750d69 100644 (file)
@@ -23,6 +23,7 @@ import org.sonar.core.platform.Module;
 import org.sonar.server.usertoken.ws.GenerateAction;
 import org.sonar.server.usertoken.ws.RevokeAction;
 import org.sonar.server.usertoken.ws.SearchAction;
+import org.sonar.server.usertoken.ws.UserTokenSupport;
 import org.sonar.server.usertoken.ws.UserTokensWs;
 
 public class UserTokenModule extends Module {
@@ -30,6 +31,7 @@ public class UserTokenModule extends Module {
   protected void configureModule() {
     add(
       UserTokensWs.class,
+      UserTokenSupport.class,
       GenerateAction.class,
       RevokeAction.class,
       SearchAction.class,
index 7f880d4cf1399f8fc9c2f7872031fb6abb6774f0..7b981fd017d7f1c18c9e9240022af52a3e7ee7ca 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.usertoken.ws;
 
-import com.google.common.base.Optional;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -29,32 +28,32 @@ import org.sonar.db.DbSession;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserTokenDto;
 import org.sonar.server.exceptions.ServerException;
-import org.sonar.server.user.UserSession;
 import org.sonar.server.usertoken.TokenGenerator;
 import org.sonarqube.ws.UserTokens;
 import org.sonarqube.ws.UserTokens.GenerateWsResponse;
 
 import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
-import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.ACTION_GENERATE;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_NAME;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.ACTION_GENERATE;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
 import static org.sonar.server.ws.WsUtils.checkRequest;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class GenerateAction implements UserTokensWsAction {
+
   private static final int MAX_TOKEN_NAME_LENGTH = 100;
+
   private final DbClient dbClient;
-  private final UserSession userSession;
   private final System2 system;
   private final TokenGenerator tokenGenerator;
+  private final UserTokenSupport userTokenSupport;
 
-  public GenerateAction(DbClient dbClient, UserSession userSession, System2 system, TokenGenerator tokenGenerator) {
-    this.userSession = userSession;
+  public GenerateAction(DbClient dbClient, System2 system, TokenGenerator tokenGenerator, UserTokenSupport userTokenSupport) {
     this.dbClient = dbClient;
     this.system = system;
     this.tokenGenerator = tokenGenerator;
+    this.userTokenSupport = userTokenSupport;
   }
 
   @Override
@@ -81,103 +80,61 @@ public class GenerateAction implements UserTokensWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    UserTokens.GenerateWsResponse generateWsResponse = doHandle(toCreateWsRequest(request));
+    UserTokens.GenerateWsResponse generateWsResponse = doHandle(request);
     writeProtobuf(generateWsResponse, request, response);
   }
 
-  private UserTokens.GenerateWsResponse doHandle(GenerateRequest request) {
+  private UserTokens.GenerateWsResponse doHandle(Request request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      checkWsRequest(dbSession, request);
-      TokenPermissionsValidator.validate(userSession, request.getLogin());
+      String name = getName(request);
+      UserDto user = userTokenSupport.getUser(dbSession, request);
+      checkTokenDoesNotAlreadyExists(dbSession, user, name);
 
       String token = tokenGenerator.generate();
       String tokenHash = hashToken(dbSession, token);
-
-      UserTokenDto userTokenDto = insertTokenInDb(dbSession, request, tokenHash);
-
-      return buildResponse(userTokenDto, token);
+      UserTokenDto userTokenDto = insertTokenInDb(dbSession, user, name, tokenHash);
+      return buildResponse(userTokenDto, token, user);
     }
   }
 
   private String hashToken(DbSession dbSession, String token) {
     String tokenHash = tokenGenerator.hash(token);
-    Optional<UserTokenDto> userToken = dbClient.userTokenDao().selectByTokenHash(dbSession, tokenHash);
-    if (userToken.isPresent()) {
-      throw new ServerException(HTTP_INTERNAL_ERROR, "Error while generating token. Please try again.");
+    UserTokenDto userToken = dbClient.userTokenDao().selectByTokenHash(dbSession, tokenHash);
+    if (userToken == null) {
+      return tokenHash;
     }
-
-    return tokenHash;
+    throw new ServerException(HTTP_INTERNAL_ERROR, "Error while generating token. Please try again.");
   }
 
-  private void checkWsRequest(DbSession dbSession, GenerateRequest request) {
-    checkLoginExists(dbSession, request);
-
-    Optional<UserTokenDto> userTokenDto = dbClient.userTokenDao().selectByLoginAndName(dbSession, request.getLogin(), request.getName());
-    checkRequest(!userTokenDto.isPresent(), "A user token with login '%s' and name '%s' already exists", request.getLogin(), request.getName());
+  private void checkTokenDoesNotAlreadyExists(DbSession dbSession, UserDto user, String name) {
+    UserTokenDto userTokenDto = dbClient.userTokenDao().selectByUserAndName(dbSession, user, name);
+    checkRequest(userTokenDto == null, "A user token for login '%s' and name '%s' already exists", user.getLogin(), name);
   }
 
-  private void checkLoginExists(DbSession dbSession, GenerateRequest request) {
-    UserDto user = dbClient.userDao().selectByLogin(dbSession, request.getLogin());
-    if (user == null) {
-      throw insufficientPrivilegesException();
-    }
+  private static String getName(Request request) {
+    String name = request.mandatoryParam(PARAM_NAME).trim();
+    checkRequest(!name.isEmpty(), "The '%s' parameter must not be blank", PARAM_NAME);
+    return name;
   }
 
-  private UserTokenDto insertTokenInDb(DbSession dbSession, GenerateRequest request, String tokenHash) {
+  private UserTokenDto insertTokenInDb(DbSession dbSession, UserDto user, String name, String tokenHash) {
     UserTokenDto userTokenDto = new UserTokenDto()
-      .setLogin(request.getLogin())
-      .setName(request.getName())
+      .setUserUuid(user.getUuid())
+      .setName(name)
       .setTokenHash(tokenHash)
       .setCreatedAt(system.now());
-
     dbClient.userTokenDao().insert(dbSession, userTokenDto);
     dbSession.commit();
     return userTokenDto;
   }
 
-  private GenerateRequest toCreateWsRequest(Request request) {
-    GenerateRequest generateWsRequest = new GenerateRequest()
-      .setLogin(request.param(PARAM_LOGIN))
-      .setName(request.mandatoryParam(PARAM_NAME).trim());
-    if (generateWsRequest.getLogin() == null) {
-      generateWsRequest.setLogin(userSession.getLogin());
-    }
-
-    checkRequest(!generateWsRequest.getName().isEmpty(), "The '%s' parameter must not be blank", PARAM_NAME);
-
-    return generateWsRequest;
-  }
-
-  private static GenerateWsResponse buildResponse(UserTokenDto userTokenDto, String token) {
+  private static GenerateWsResponse buildResponse(UserTokenDto userTokenDto, String token, UserDto user) {
     return UserTokens.GenerateWsResponse.newBuilder()
-      .setLogin(userTokenDto.getLogin())
+      .setLogin(user.getLogin())
       .setName(userTokenDto.getName())
       .setCreatedAt(formatDateTime(userTokenDto.getCreatedAt()))
       .setToken(token)
       .build();
   }
 
-  private static class GenerateRequest {
-
-    private String login;
-    private String name;
-
-    public GenerateRequest setLogin(String login) {
-      this.login = login;
-      return this;
-    }
-
-    public String getLogin() {
-      return login;
-    }
-
-    public GenerateRequest setName(String name) {
-      this.name = name;
-      return this;
-    }
-
-    public String getName() {
-      return name;
-    }
-  }
 }
index 303e29fdd638fd0550ced9df9e679e731dfcc712..20e6e116ffffd5d8bc2749e11bd17fee9d292272 100644 (file)
@@ -24,25 +24,26 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.server.user.UserSession;
+import org.sonar.db.user.UserDto;
 
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.ACTION_REVOKE;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_NAME;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.ACTION_REVOKE;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
 
 public class RevokeAction implements UserTokensWsAction {
+
   private final DbClient dbClient;
-  private final UserSession userSession;
+  private final UserTokenSupport userTokenSupport;
 
-  public RevokeAction(DbClient dbClient, UserSession userSession) {
+  public RevokeAction(DbClient dbClient, UserTokenSupport userTokenSupport) {
     this.dbClient = dbClient;
-    this.userSession = userSession;
+    this.userTokenSupport = userTokenSupport;
   }
 
   @Override
   public void define(WebService.NewController context) {
     WebService.NewAction action = context.createAction(ACTION_REVOKE)
-      .setDescription("Revoke a user access token. <br/>"+
+      .setDescription("Revoke a user access token. <br/>" +
         "If the login is set, it requires administration permissions. Otherwise, a token is generated for the authenticated user.")
       .setSince("5.3")
       .setPost(true)
@@ -60,16 +61,10 @@ public class RevokeAction implements UserTokensWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    String login = request.param(PARAM_LOGIN);
-    if (login == null) {
-      login = userSession.getLogin();
-    }
     String name = request.mandatoryParam(PARAM_NAME);
-
-    TokenPermissionsValidator.validate(userSession, login);
-
     try (DbSession dbSession = dbClient.openSession(false)) {
-      dbClient.userTokenDao().deleteByLoginAndName(dbSession, login, name);
+      UserDto user = userTokenSupport.getUser(dbSession, request);
+      dbClient.userTokenDao().deleteByUserAndName(dbSession, user, name);
       dbSession.commit();
     }
     response.noContent();
index 4ead6902c207c13a3a4d30e0607fce7fad16ecf6..87870d465798026e2dc5b78874f4279ec41088d5 100644 (file)
@@ -25,23 +25,23 @@ import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserTokenDto;
-import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.UserTokens.SearchWsResponse;
 
 import static org.sonar.api.utils.DateUtils.formatDateTime;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.ACTION_SEARCH;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonar.server.ws.WsUtils.checkFound;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.ACTION_SEARCH;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class SearchAction implements UserTokensWsAction {
+
   private final DbClient dbClient;
-  private final UserSession userSession;
+  private final UserTokenSupport userTokenSupport;
 
-  public SearchAction(DbClient dbClient, UserSession userSession) {
+  public SearchAction(DbClient dbClient, UserTokenSupport userTokenSupport) {
     this.dbClient = dbClient;
-    this.userSession = userSession;
+    this.userTokenSupport = userTokenSupport;
   }
 
   @Override
@@ -61,28 +61,22 @@ public class SearchAction implements UserTokensWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    String login = request.param(PARAM_LOGIN);
-    if (login == null) {
-      login = userSession.getLogin();
-    }
-    SearchWsResponse searchWsResponse = doHandle(login);
+    SearchWsResponse searchWsResponse = doHandle(request);
     writeProtobuf(searchWsResponse, request, response);
   }
 
-  private SearchWsResponse doHandle(String login) {
-    TokenPermissionsValidator.validate(userSession, login);
-
+  private SearchWsResponse doHandle(Request request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      checkLoginExists(dbSession, login);
-      List<UserTokenDto> userTokens = dbClient.userTokenDao().selectByLogin(dbSession, login);
-      return buildResponse(login, userTokens);
+      UserDto user = userTokenSupport.getUser(dbSession, request);
+      List<UserTokenDto> userTokens = dbClient.userTokenDao().selectByUser(dbSession, user);
+      return buildResponse(user, userTokens);
     }
   }
 
-  private static SearchWsResponse buildResponse(String login, List<UserTokenDto> userTokensDto) {
+  private static SearchWsResponse buildResponse(UserDto user, List<UserTokenDto> userTokensDto) {
     SearchWsResponse.Builder searchWsResponse = SearchWsResponse.newBuilder();
     SearchWsResponse.UserToken.Builder userTokenBuilder = SearchWsResponse.UserToken.newBuilder();
-    searchWsResponse.setLogin(login);
+    searchWsResponse.setLogin(user.getLogin());
     for (UserTokenDto userTokenDto : userTokensDto) {
       userTokenBuilder
         .clear()
@@ -94,7 +88,4 @@ public class SearchAction implements UserTokensWsAction {
     return searchWsResponse.build();
   }
 
-  private void checkLoginExists(DbSession dbSession, String login) {
-    checkFound(dbClient.userDao().selectByLogin(dbSession, login), "User with login '%s' not found", login);
-  }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java
deleted file mode 100644 (file)
index 5aec810..0000000
+++ /dev/null
@@ -1,42 +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.usertoken.ws;
-
-import javax.annotation.Nullable;
-import org.sonar.server.user.UserSession;
-
-import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-
-class TokenPermissionsValidator {
-  private TokenPermissionsValidator() {
-    // prevent instantiation
-  }
-
-  static void validate(UserSession userSession, @Nullable String requestLogin) {
-    userSession.checkLoggedIn();
-    if (!userSession.isSystemAdministrator() && !isLoggedInUser(userSession, requestLogin)) {
-      throw insufficientPrivilegesException();
-    }
-  }
-
-  private static boolean isLoggedInUser(UserSession userSession, @Nullable String requestLogin) {
-    return requestLogin != null && requestLogin.equals(userSession.getLogin());
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokenSupport.java b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/UserTokenSupport.java
new file mode 100644 (file)
index 0000000..be5545f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.usertoken.ws;
+
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Request;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Objects.requireNonNull;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonar.server.ws.WsUtils.checkFound;
+
+public class UserTokenSupport {
+
+  static final String CONTROLLER = "api/user_tokens";
+  static final String ACTION_SEARCH = "search";
+  static final String ACTION_REVOKE = "revoke";
+  static final String ACTION_GENERATE = "generate";
+  static final String PARAM_LOGIN = "login";
+  static final String PARAM_NAME = "name";
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+
+  public UserTokenSupport(DbClient dbClient, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+  }
+
+  UserDto getUser(DbSession dbSession, Request request) {
+    String login = request.param(PARAM_LOGIN);
+    login = login == null ? userSession.getLogin() : login;
+    validate(userSession, login);
+    UserDto user = dbClient.userDao().selectByLogin(dbSession, requireNonNull(login, "Login should not be null"));
+    checkFound(user, "User with login '%s' doesn't exist", login);
+    return user;
+  }
+
+  private static void validate(UserSession userSession, @Nullable String requestLogin) {
+    userSession.checkLoggedIn();
+    if (userSession.isSystemAdministrator() || isLoggedInUser(userSession, requestLogin)) {
+      return;
+    }
+    throw insufficientPrivilegesException();
+  }
+
+  private static boolean isLoggedInUser(UserSession userSession, @Nullable String requestLogin) {
+    return requestLogin != null && requestLogin.equals(userSession.getLogin());
+  }
+}
index 7cd28cff6ea295bfa2f5dccc764430d135d84ecc..60888d55e01362b233b553f20c2287f9fb78d5c0 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.usertoken.ws;
 
 import org.sonar.api.server.ws.WebService;
 
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.CONTROLLER;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.CONTROLLER;
 
 public class UserTokensWs implements WebService {
   private final UserTokensWsAction[] actions;
index d1d41d6426a5b11be3e90e696dc4a29e49949a0f..3d0fb5e16bc48fbe08bacc97c87d916377118559 100644 (file)
 package org.sonar.server.usertoken.ws;
 
 public class UserTokensWsParameters {
-  public static final String CONTROLLER = "api/user_tokens";
-  public static final String ACTION_GENERATE = "generate";
-  public static final String ACTION_REVOKE = "revoke";
-  public static final String ACTION_SEARCH = "search";
-
-  public static final String PARAM_LOGIN = "login";
-  public static final String PARAM_NAME = "name";
 
   private UserTokensWsParameters() {
     // constants only
index 8d89727aaa37fea9ecf5da4ed21a74112b108d8f..17bc1f0928845448a5b02e3095d414d47c62aefd 100644 (file)
@@ -62,11 +62,11 @@ public class BasicAuthenticatorTest {
   public ExpectedException expectedException = none();
 
   @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private DbClient dbClient = dbTester.getDbClient();
+  private DbClient dbClient = db.getDbClient();
 
-  private DbSession dbSession = dbTester.getSession();
+  private DbSession dbSession = db.getSession();
 
   private CredentialsAuthenticator credentialsAuthenticator = mock(CredentialsAuthenticator.class);
   private UserTokenAuthenticator userTokenAuthenticator = mock(UserTokenAuthenticator.class);
@@ -139,20 +139,19 @@ public class BasicAuthenticatorTest {
 
   @Test
   public void authenticate_from_user_token() {
-    insertUser(UserTesting.newUserDto().setLogin(LOGIN));
-    when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of(LOGIN));
+    UserDto user = db.users().insertUser();
+    when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of(user.getUuid()));
     when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
 
-    Optional<UserDto> userDto = underTest.authenticate(request);
+    Optional<UserDto> userAuthenticated = underTest.authenticate(request);
 
-    assertThat(userDto.isPresent()).isTrue();
-    assertThat(userDto.get().getLogin()).isEqualTo(LOGIN);
-    verify(authenticationEvent).loginSuccess(request, LOGIN, Source.local(BASIC_TOKEN));
+    assertThat(userAuthenticated.isPresent()).isTrue();
+    assertThat(userAuthenticated.get().getLogin()).isEqualTo(user.getLogin());
+    verify(authenticationEvent).loginSuccess(request, user.getLogin(), Source.local(BASIC_TOKEN));
   }
 
   @Test
   public void does_not_authenticate_from_user_token_when_token_is_invalid() {
-    insertUser(UserTesting.newUserDto().setLogin(LOGIN));
     when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.empty());
     when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
 
@@ -165,8 +164,7 @@ public class BasicAuthenticatorTest {
   }
 
   @Test
-  public void does_not_authenticate_from_user_token_when_token_does_not_match_active_user() {
-    insertUser(UserTesting.newUserDto().setLogin(LOGIN));
+  public void does_not_authenticate_from_user_token_when_token_does_not_match_existing_user() {
     when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of("Unknown user"));
     when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
 
@@ -178,10 +176,18 @@ public class BasicAuthenticatorTest {
     }
   }
 
-  private UserDto insertUser(UserDto userDto) {
-    dbClient.userDao().insert(dbSession, userDto);
-    dbSession.commit();
-    return userDto;
+  @Test
+  public void does_not_authenticate_from_user_token_when_token_does_not_match_active_user() {
+    UserDto user = db.users().insertDisabledUser();
+    when(userTokenAuthenticator.authenticate("token")).thenReturn(Optional.of(user.getUuid()));
+    when(request.getHeader("Authorization")).thenReturn("Basic " + toBase64("token:"));
+
+    expectedException.expect(authenticationException().from(Source.local(Method.BASIC_TOKEN)).withoutLogin().andNoPublicMessage());
+    try {
+      underTest.authenticate(request);
+    } finally {
+      verifyZeroInteractions(authenticationEvent);
+    }
   }
 
   private static String toBase64(String text) {
index c5379de6bb33ca8bb0dcda2fed7e64d27ad3daf7..04e4126c093d3a6a268b4c48d419e81485b33f1c 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.user.ws;
 
 import java.util.Optional;
+import java.util.function.Consumer;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -62,8 +63,6 @@ import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.db.permission.OrganizationPermission.SCAN;
 import static org.sonar.db.property.PropertyTesting.newUserPropertyDto;
-import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
 import static org.sonar.server.user.index.UserIndexDefinition.FIELD_ACTIVE;
 import static org.sonar.server.user.index.UserIndexDefinition.FIELD_UUID;
 import static org.sonar.test.JsonAssert.assertJson;
@@ -94,7 +93,7 @@ public class DeactivateActionTest {
 
   @Test
   public void deactivate_user_and_delete_his_related_data() {
-    UserDto user = insertUser(newUserDto()
+    UserDto user = insertUser(u -> u
       .setLogin("ada.lovelace")
       .setEmail("ada.lovelace@noteg.com")
       .setName("Ada Lovelace")
@@ -114,7 +113,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_group_membership() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     GroupDto group1 = db.users().insertGroup();
     db.users().insertGroup();
     db.users().insertMember(group1, user);
@@ -127,20 +126,20 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_tokens() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
-    db.getDbClient().userTokenDao().insert(dbSession, newUserToken().setLogin(user.getLogin()));
-    db.getDbClient().userTokenDao().insert(dbSession, newUserToken().setLogin(user.getLogin()));
+    UserDto user = insertUser();
+    db.users().insertToken(user);
+    db.users().insertToken(user);
     db.commit();
 
     deactivate(user.getLogin()).getInput();
 
-    assertThat(db.getDbClient().userTokenDao().selectByLogin(dbSession, user.getLogin())).isEmpty();
+    assertThat(db.getDbClient().userTokenDao().selectByUser(dbSession, user)).isEmpty();
   }
 
   @Test
   public void deactivate_user_deletes_his_properties() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     ComponentDto project = db.components().insertPrivateProject();
     db.properties().insertProperty(newUserPropertyDto(user));
     db.properties().insertProperty(newUserPropertyDto(user));
@@ -155,7 +154,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_permissions() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     ComponentDto project = db.components().insertPrivateProject();
     db.users().insertPermissionOnUser(user, SCAN);
     db.users().insertPermissionOnUser(user, ADMINISTER_QUALITY_PROFILES);
@@ -171,7 +170,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_permission_templates() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     PermissionTemplateDto template = db.permissionTemplates().insertTemplate();
     PermissionTemplateDto anotherTemplate = db.permissionTemplates().insertTemplate();
     db.permissionTemplates().addUserToTemplate(template.getId(), user.getId(), USER);
@@ -187,7 +186,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_qprofiles_permissions() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization());
     db.qualityProfiles().addUserPermission(profile, user);
 
@@ -199,7 +198,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_default_assignee_settings() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     ComponentDto project = db.components().insertPrivateProject();
     ComponentDto anotherProject = db.components().insertPrivateProject();
     db.properties().insertProperty(new PropertyDto().setKey("sonar.issues.defaultAssigneeLogin").setValue(user.getLogin()).setResourceId(project.getId()));
@@ -215,7 +214,7 @@ public class DeactivateActionTest {
   @Test
   public void deactivate_user_deletes_his_organization_membership() {
     logInAsSystemAdministrator();
-    UserDto user = insertUser(newUserDto());
+    UserDto user = insertUser();
     OrganizationDto organization = db.organizations().insert();
     db.organizations().addMember(organization, user);
     OrganizationDto anotherOrganization = db.organizations().insert();
@@ -294,7 +293,7 @@ public class DeactivateActionTest {
   public void fail_to_deactivate_last_administrator_of_organization() {
     // user1 is the unique administrator of org1 and org2.
     // user1 and user2 are both administrators of org3
-    UserDto user1 = insertUser(newUserDto().setLogin("test"));
+    UserDto user1 = insertUser(u -> u.setLogin("test"));
     OrganizationDto org1 = db.organizations().insert(newOrganizationDto().setKey("org1"));
     OrganizationDto org2 = db.organizations().insert(newOrganizationDto().setKey("org2"));
     OrganizationDto org3 = db.organizations().insert(newOrganizationDto().setKey("org3"));
@@ -335,7 +334,7 @@ public class DeactivateActionTest {
 
   @Test
   public void test_example() {
-    UserDto user = insertUser(newUserDto()
+    UserDto user = insertUser(u -> u
       .setLogin("ada.lovelace")
       .setEmail("ada.lovelace@noteg.com")
       .setName("Ada Lovelace")
@@ -349,13 +348,14 @@ public class DeactivateActionTest {
   }
 
   private UserDto createUser() {
-    return insertUser(newUserDto());
+    return insertUser();
   }
 
-  private UserDto insertUser(UserDto user) {
-    dbClient.userDao().insert(dbSession, user);
-    dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(user.getLogin()));
-    dbClient.propertiesDao().saveProperty(dbSession, new PropertyDto().setUserId(user.getId()).setKey("foo").setValue("bar"));
+  @SafeVarargs
+  private final UserDto insertUser(Consumer<UserDto>... populators) {
+    UserDto user = db.users().insertUser(populators);
+    db.users().insertToken(user);
+    db.properties().insertProperties(new PropertyDto().setUserId(user.getId()).setKey("foo").setValue("bar"));
     userIndexer.commitAndIndex(dbSession, user);
     return user;
   }
index 86f1d5a258f12e0d37e0a3794a9dafb857dfbf41..2e6f056e861455338e9b4d0dff879eb2aac1de50 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.user.ws;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.stream.IntStream;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService.Param;
@@ -32,7 +33,6 @@ import org.sonar.db.DbTester;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserGroupDto;
-import org.sonar.db.user.UserTesting;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.issue.ws.AvatarResolverImpl;
 import org.sonar.server.tester.UserSessionRule;
@@ -45,7 +45,6 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.db.user.GroupTesting.newGroupDto;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
 import static org.sonar.test.JsonAssert.assertJson;
 
 public class SearchActionTest {
@@ -86,12 +85,11 @@ public class SearchActionTest {
     dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(simon.getId()).setGroupId(sonarUsers.getId()));
     dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(fmallet.getId()).setGroupId(sonarUsers.getId()));
     dbClient.userGroupDao().insert(dbSession, new UserGroupDto().setUserId(fmallet.getId()).setGroupId(sonarAdministrators.getId()));
-
+    db.commit();
     for (int i = 0; i < 3; i++) {
-      dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(simon.getLogin()));
+      db.users().insertToken(simon);
     }
-    dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(fmallet.getLogin()));
-    db.commit();
+    db.users().insertToken(fmallet);
     userIndexer.indexOnStartup(null);
     loginAsSystemAdministrator();
 
@@ -297,15 +295,10 @@ public class SearchActionTest {
         .setExternalIdentityProvider("sonarqube"));
       userDtos.add(userDto);
 
-      for (int tokenIndex = 0; tokenIndex < index; tokenIndex++) {
-        dbClient.userTokenDao().insert(dbSession, newUserToken()
-          .setLogin(login)
-          .setName(String.format("%s-%d", login, tokenIndex)));
-      }
+      IntStream.range(0, index).forEach(i -> db.users().insertToken(userDto, t -> t.setName(String.format("%s-%d", login, i))));
       db.users().insertMember(group1, userDto);
       db.users().insertMember(group2, userDto);
     }
-    dbSession.commit();
     userIndexer.indexOnStartup(null);
     return userDtos;
   }
index 01578f9dc70b4bdf0245d7314e6570a24843a8b7..611a4553e1d13735294b05b8e77993374fc0140e 100644 (file)
  */
 package org.sonar.server.usertoken;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
-
 import java.util.Optional;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class UserTokenAuthenticatorTest {
-  static final String GRACE_HOPPER = "grace.hopper";
-  static final String ADA_LOVELACE = "ada.lovelace";
 
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
-  DbClient dbClient = db.getDbClient();
-  DbSession dbSession = db.getSession();
-  TokenGenerator tokenGenerator = mock(TokenGenerator.class);
 
-  UserTokenAuthenticator underTest = new UserTokenAuthenticator(tokenGenerator, db.getDbClient());
+  private TokenGenerator tokenGenerator = mock(TokenGenerator.class);
+
+  private UserTokenAuthenticator underTest = new UserTokenAuthenticator(tokenGenerator, db.getDbClient());
 
   @Test
   public void return_login_when_token_hash_found_in_db() {
     String token = "known-token";
     String tokenHash = "123456789";
     when(tokenGenerator.hash(token)).thenReturn(tokenHash);
-    dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(GRACE_HOPPER).setTokenHash(tokenHash));
-    dbClient.userTokenDao().insert(dbSession, newUserToken().setLogin(ADA_LOVELACE).setTokenHash("another-token-hash"));
-    db.commit();
+    UserDto user1 = db.users().insertUser();
+    db.users().insertToken(user1, t -> t.setTokenHash(tokenHash));
+    UserDto user2 = db.users().insertUser();
+    db.users().insertToken(user2, t -> t.setTokenHash("another-token-hash"));
 
     Optional<String> login = underTest.authenticate(token);
 
     assertThat(login.isPresent()).isTrue();
-    assertThat(login.get()).isEqualTo(GRACE_HOPPER);
+    assertThat(login.get()).isEqualTo(user1.getUuid());
   }
 
   @Test
index 3f98207a1100754c551ba26c0af2f22171638040..657ffb14b0e199983d3d69fefc130b581e491978 100644 (file)
@@ -29,6 +29,6 @@ public class UserTokenModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new UserTokenModule().configure(container);
-    assertThat(container.size()).isEqualTo(8);
+    assertThat(container.size()).isEqualTo(9);
   }
 }
index 8753fdb3680802c29d110b0927b4d72430794e2f..5beeeb5ba3e6892ca881ffc4d1378585695f7507 100644 (file)
@@ -26,8 +26,10 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.exceptions.ServerException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.tester.UserSessionRule;
@@ -41,15 +43,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
 import static org.sonar.test.JsonAssert.assertJson;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_NAME;
 
 public class GenerateActionTest {
-  private static final String GRACE_HOPPER = "grace.hopper";
-  private static final String ADA_LOVELACE = "ada.lovelace";
+
   private static final String TOKEN_NAME = "Third Party Application";
 
   @Rule
@@ -60,26 +59,25 @@ public class GenerateActionTest {
   public ExpectedException expectedException = ExpectedException.none();
 
   private TokenGenerator tokenGenerator = mock(TokenGenerator.class);
-  private WsActionTester ws;
+
+  private WsActionTester ws = new WsActionTester(
+    new GenerateAction(db.getDbClient(), System2.INSTANCE, tokenGenerator, new UserTokenSupport(db.getDbClient(), userSession)));
 
   @Before
   public void setUp() {
     when(tokenGenerator.generate()).thenReturn("123456789");
     when(tokenGenerator.hash(anyString())).thenReturn("987654321");
-    db.users().insertUser(newUserDto().setLogin(GRACE_HOPPER));
-    db.users().insertUser(newUserDto().setLogin(ADA_LOVELACE));
-
-    ws = new WsActionTester(
-      new GenerateAction(db.getDbClient(), userSession, System2.INSTANCE, tokenGenerator));
   }
 
   @Test
   public void json_example() {
+    UserDto user1 = db.users().insertUser(u -> u.setLogin("grace.hopper"));
+    UserDto user2 = db.users().insertUser(u -> u.setLogin("ada.lovelace"));
     logInAsSystemAdministrator();
 
     String response = ws.newRequest()
       .setMediaType(MediaTypes.JSON)
-      .setParam(PARAM_LOGIN, GRACE_HOPPER)
+      .setParam(PARAM_LOGIN, user1.getLogin())
       .setParam(PARAM_NAME, TOKEN_NAME)
       .execute().getInput();
 
@@ -88,11 +86,12 @@ public class GenerateActionTest {
 
   @Test
   public void a_user_can_generate_token_for_himself() {
-    userSession.logIn(GRACE_HOPPER);
+    UserDto user = db.users().insertUser();
+    userSession.logIn(user);
 
     GenerateWsResponse response = newRequest(null, TOKEN_NAME);
 
-    assertThat(response.getLogin()).isEqualTo(GRACE_HOPPER);
+    assertThat(response.getLogin()).isEqualTo(user.getLogin());
     assertThat(response.getCreatedAt()).isNotEmpty();
   }
 
@@ -100,61 +99,66 @@ public class GenerateActionTest {
   public void fail_if_login_does_not_exist() {
     logInAsSystemAdministrator();
 
-    expectedException.expect(ForbiddenException.class);
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("User with login 'unknown-login' doesn't exist");
 
     newRequest("unknown-login", "any-name");
   }
 
   @Test
   public void fail_if_name_is_blank() {
+    UserDto user = db.users().insertUser();
     logInAsSystemAdministrator();
 
     expectedException.expect(BadRequestException.class);
     expectedException.expectMessage("The 'name' parameter must not be blank");
 
-    newRequest(GRACE_HOPPER, "   ");
+    newRequest(user.getLogin(), "   ");
   }
 
   @Test
   public void fail_if_token_with_same_login_and_name_exists() {
+    UserDto user = db.users().insertUser();
     logInAsSystemAdministrator();
+    db.users().insertToken(user, t -> t.setName(TOKEN_NAME));
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
     expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("A user token with login 'grace.hopper' and name 'Third Party Application' already exists");
+    expectedException.expectMessage(String.format("A user token for login '%s' and name 'Third Party Application' already exists", user.getLogin()));
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), TOKEN_NAME);
   }
 
   @Test
   public void fail_if_token_hash_already_exists_in_db() {
+    UserDto user = db.users().insertUser();
     logInAsSystemAdministrator();
-
     when(tokenGenerator.hash(anyString())).thenReturn("987654321");
-    db.getDbClient().userTokenDao().insert(db.getSession(), newUserToken().setTokenHash("987654321"));
-    db.commit();
+    db.users().insertToken(user, t -> t.setTokenHash("987654321"));
+
     expectedException.expect(ServerException.class);
     expectedException.expectMessage("Error while generating token. Please try again.");
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), TOKEN_NAME);
   }
 
   @Test
   public void throw_ForbiddenException_if_non_administrator_creates_token_for_someone_else() {
+    UserDto user = db.users().insertUser();
     userSession.logIn().setNonSystemAdministrator();
 
     expectedException.expect(ForbiddenException.class);
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), TOKEN_NAME);
   }
 
   @Test
   public void throw_UnauthorizedException_if_not_logged_in() {
+    UserDto user = db.users().insertUser();
     userSession.anonymous();
 
     expectedException.expect(UnauthorizedException.class);
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), TOKEN_NAME);
   }
 
   private GenerateWsResponse newRequest(@Nullable String login, String name) {
index 1ae6a1f0ba5baf8bc08ba67ad55072c5a097b67e..8e0611f9b09f1bf4cff416db31bcefdec672f09b 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.usertoken.ws;
 
 import javax.annotation.Nullable;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -28,23 +27,20 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserTokenDto;
 import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.exceptions.UnauthorizedException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_NAME;
-
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
 
 public class RevokeActionTest {
-  private static final String GRACE_HOPPER = "grace.hopper";
-  private static final String ADA_LOVELACE = "ada.lovelace";
-  private static final String TOKEN_NAME = "token-name";
 
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
@@ -54,67 +50,82 @@ public class RevokeActionTest {
   public ExpectedException expectedException = ExpectedException.none();
 
   private DbClient dbClient = db.getDbClient();
-  private final DbSession dbSession = db.getSession();
-  private WsActionTester ws;
-
-  @Before
-  public void setUp() {
-    ws = new WsActionTester(
-      new RevokeAction(dbClient, userSession));
-  }
+  private DbSession dbSession = db.getSession();
+  private WsActionTester ws = new WsActionTester(new RevokeAction(dbClient, new UserTokenSupport(db.getDbClient(), userSession)));
 
   @Test
   public void delete_token_in_db() {
     logInAsSystemAdministrator();
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName("token-to-delete"));
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName("token-to-keep-1"));
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName("token-to-keep-2"));
-    insertUserToken(newUserToken().setLogin(ADA_LOVELACE).setName("token-to-delete"));
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    UserTokenDto tokenToDelete = db.users().insertToken(user1);
+    UserTokenDto tokenToKeep1 = db.users().insertToken(user1);
+    UserTokenDto tokenToKeep2 = db.users().insertToken(user1);
+    UserTokenDto tokenFromAnotherUser = db.users().insertToken(user2);
 
-    String response = newRequest(GRACE_HOPPER, "token-to-delete");
+    String response = newRequest(user1.getLogin(), tokenToDelete.getName());
 
     assertThat(response).isEmpty();
-    assertThat(dbClient.userTokenDao().selectByLogin(dbSession, GRACE_HOPPER)).extracting("name").containsOnly("token-to-keep-1", "token-to-keep-2");
-    assertThat(dbClient.userTokenDao().selectByLogin(dbSession, ADA_LOVELACE)).extracting("name").containsOnly("token-to-delete");
+    assertThat(dbClient.userTokenDao().selectByUser(dbSession, user1))
+      .extracting(UserTokenDto::getName)
+      .containsExactlyInAnyOrder(tokenToKeep1.getName(), tokenToKeep2.getName());
+    assertThat(dbClient.userTokenDao().selectByUser(dbSession, user2))
+      .extracting(UserTokenDto::getName)
+      .containsExactlyInAnyOrder(tokenFromAnotherUser.getName());
   }
 
   @Test
   public void user_can_delete_its_own_tokens() {
-    userSession.logIn(GRACE_HOPPER);
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName("token-to-delete"));
+    UserDto user = db.users().insertUser();
+    UserTokenDto token = db.users().insertToken(user);
+    userSession.logIn(user);
 
-    String response = newRequest(null, "token-to-delete");
+    String response = newRequest(null, token.getName());
 
     assertThat(response).isEmpty();
-    assertThat(dbClient.userTokenDao().selectByLogin(dbSession, GRACE_HOPPER)).isEmpty();
+    assertThat(dbClient.userTokenDao().selectByUser(dbSession, user)).isEmpty();
   }
 
   @Test
   public void does_not_fail_when_incorrect_login_or_name() {
+    UserDto user = db.users().insertUser();
+    db.users().insertToken(user);
+
     logInAsSystemAdministrator();
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName(TOKEN_NAME));
 
-    newRequest(ADA_LOVELACE, "another-token-name");
+    newRequest(user.getLogin(), "another-token-name");
   }
 
   @Test
   public void throw_ForbiddenException_if_non_administrator_revokes_token_of_someone_else() {
+    UserDto user = db.users().insertUser();
+    UserTokenDto token = db.users().insertToken(user);
     userSession.logIn();
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName(TOKEN_NAME));
 
     expectedException.expect(ForbiddenException.class);
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), token.getName());
   }
 
   @Test
   public void throw_UnauthorizedException_if_not_logged_in() {
+    UserDto user = db.users().insertUser();
+    UserTokenDto token = db.users().insertToken(user);
     userSession.anonymous();
-    insertUserToken(newUserToken().setLogin(GRACE_HOPPER).setName(TOKEN_NAME));
 
     expectedException.expect(UnauthorizedException.class);
 
-    newRequest(GRACE_HOPPER, TOKEN_NAME);
+    newRequest(user.getLogin(), token.getName());
+  }
+
+  @Test
+  public void fail_if_login_does_not_exist() {
+    logInAsSystemAdministrator();
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("User with login 'unknown-login' doesn't exist");
+
+    newRequest("unknown-login", "any-name");
   }
 
   private String newRequest(@Nullable String login, String name) {
@@ -127,11 +138,6 @@ public class RevokeActionTest {
     return testRequest.execute().getInput();
   }
 
-  private void insertUserToken(UserTokenDto userToken) {
-    dbClient.userTokenDao().insert(dbSession, userToken);
-    dbSession.commit();
-  }
-
   private void logInAsSystemAdministrator() {
     userSession.logIn().setSystemAdministrator();
   }
index 69f936f42201a143ce5cc5380a65869c9d0b1ed7..9bf8272416d51fd24e80ecdca8fd94b11470b19d 100644 (file)
 package org.sonar.server.usertoken.ws;
 
 import javax.annotation.Nullable;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.exceptions.UnauthorizedException;
@@ -37,14 +36,10 @@ import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.UserTokens.SearchWsResponse;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.user.UserTesting.newUserDto;
-import static org.sonar.db.user.UserTokenTesting.newUserToken;
+import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
 import static org.sonar.test.JsonAssert.assertJson;
-import static org.sonar.server.usertoken.ws.UserTokensWsParameters.PARAM_LOGIN;
 
 public class SearchActionTest {
-  private static final String GRACE_HOPPER = "grace.hopper";
-  private static final String ADA_LOVELACE = "ada.lovelace";
 
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
@@ -52,40 +47,22 @@ public class SearchActionTest {
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
-  private DbClient dbClient = db.getDbClient();
-  private DbSession dbSession = db.getSession();
-  private WsActionTester ws = new WsActionTester(new SearchAction(dbClient, userSession));
 
-  @Before
-  public void setUp() {
-    db.users().insertUser(newUserDto().setLogin(GRACE_HOPPER));
-    db.users().insertUser(newUserDto().setLogin(ADA_LOVELACE));
-  }
+  private DbClient dbClient = db.getDbClient();
+  private WsActionTester ws = new WsActionTester(new SearchAction(dbClient, new UserTokenSupport(db.getDbClient(), userSession)));
 
   @Test
   public void search_json_example() {
+    UserDto user1 = db.users().insertUser(u -> u.setLogin("grace.hopper"));
+    UserDto user2 = db.users().insertUser(u -> u.setLogin("ada.lovelace"));
+    db.users().insertToken(user1, t -> t.setName("Project scan on Travis").setCreatedAt(1448523067221L));
+    db.users().insertToken(user1, t -> t.setName("Project scan on AppVeyor").setCreatedAt(1438523067221L));
+    db.users().insertToken(user1, t -> t.setName("Project scan on Jenkins").setCreatedAt(1428523067221L));
+    db.users().insertToken(user2, t -> t.setName("Project scan on Travis").setCreatedAt(141456787123L));
     logInAsSystemAdministrator();
 
-    dbClient.userTokenDao().insert(dbSession, newUserToken()
-      .setCreatedAt(1448523067221L)
-      .setName("Project scan on Travis")
-      .setLogin(GRACE_HOPPER));
-    dbClient.userTokenDao().insert(dbSession, newUserToken()
-      .setCreatedAt(1438523067221L)
-      .setName("Project scan on AppVeyor")
-      .setLogin(GRACE_HOPPER));
-    dbClient.userTokenDao().insert(dbSession, newUserToken()
-      .setCreatedAt(1428523067221L)
-      .setName("Project scan on Jenkins")
-      .setLogin(GRACE_HOPPER));
-    dbClient.userTokenDao().insert(dbSession, newUserToken()
-      .setCreatedAt(141456787123L)
-      .setName("Project scan on Travis")
-      .setLogin(ADA_LOVELACE));
-    dbSession.commit();
-
     String response = ws.newRequest()
-      .setParam(PARAM_LOGIN, GRACE_HOPPER)
+      .setParam(PARAM_LOGIN, user1.getLogin())
       .execute().getInput();
 
     assertJson(response).isSimilarTo(getClass().getResource("search-example.json"));
@@ -93,12 +70,9 @@ public class SearchActionTest {
 
   @Test
   public void a_user_can_search_its_own_token() {
-    userSession.logIn(GRACE_HOPPER);
-    dbClient.userTokenDao().insert(dbSession, newUserToken()
-      .setCreatedAt(1448523067221L)
-      .setName("Project scan on Travis")
-      .setLogin(GRACE_HOPPER));
-    db.commit();
+    UserDto user = db.users().insertUser();
+    db.users().insertToken(user, t -> t.setName("Project scan on Travis").setCreatedAt(1448523067221L));
+    userSession.logIn(user);
 
     SearchWsResponse response = newRequest(null);
 
@@ -110,27 +84,29 @@ public class SearchActionTest {
     logInAsSystemAdministrator();
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage("User with login 'unknown-login' not found");
+    expectedException.expectMessage("User with login 'unknown-login' doesn't exist");
 
     newRequest("unknown-login");
   }
 
   @Test
   public void throw_ForbiddenException_if_a_non_root_administrator_searches_for_tokens_of_someone_else() {
+    UserDto user = db.users().insertUser();
     userSession.logIn();
 
     expectedException.expect(ForbiddenException.class);
 
-    newRequest(GRACE_HOPPER);
+    newRequest(user.getLogin());
   }
 
   @Test
   public void throw_UnauthorizedException_if_not_logged_in() {
+    UserDto user = db.users().insertUser();
     userSession.anonymous();
 
     expectedException.expect(UnauthorizedException.class);
 
-    newRequest(GRACE_HOPPER);
+    newRequest(user.getLogin());
   }
 
   private SearchWsResponse newRequest(@Nullable String login) {
index cb8284242a824514270b8b59459214d157fdb44f..c93fce55d9a98863fe23bd9aaf5cc5974de6a271 100644 (file)
@@ -24,7 +24,6 @@ import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
-import org.sonar.server.user.UserSession;
 import org.sonar.server.usertoken.TokenGenerator;
 import org.sonar.server.ws.WsTester;
 
@@ -38,15 +37,15 @@ public class UserTokensWsTest {
 
   @Before
   public void setUp() {
-    UserSession userSession = mock(UserSession.class);
     DbClient dbClient = mock(DbClient.class);
     System2 system = mock(System2.class);
     TokenGenerator tokenGenerator = mock(TokenGenerator.class);
+    UserTokenSupport userTokenSupport = mock(UserTokenSupport.class);
 
     ws = new WsTester(new UserTokensWs(
-      new GenerateAction(dbClient, userSession, system, tokenGenerator),
-      new RevokeAction(dbClient, userSession),
-      new SearchAction(dbClient, userSession)));
+      new GenerateAction(dbClient, system, tokenGenerator, userTokenSupport),
+      new RevokeAction(dbClient, userTokenSupport),
+      new SearchAction(dbClient, userTokenSupport)));
   }
 
   @Test
index 97612a23f3aea674fea08c6dfda383324a614110..6278ab11cbc6faae2ad269458f6932a99fb301cd 100644 (file)
@@ -36,8 +36,10 @@ import org.sonarqube.ws.Organizations.Organization;
 import org.sonarqube.ws.Projects;
 import org.sonarqube.ws.Qualityprofiles;
 import org.sonarqube.ws.Settings;
+import org.sonarqube.ws.UserTokens;
 import org.sonarqube.ws.Users;
 import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsClient;
 import org.sonarqube.ws.client.issues.AssignRequest;
 import org.sonarqube.ws.client.organizations.AddMemberRequest;
 import org.sonarqube.ws.client.organizations.SearchRequest;
@@ -234,6 +236,32 @@ public class SonarCloudUpdateLoginDuringAuthenticationTest {
       false);
   }
 
+  @Test
+  public void user_tokens_after_login_update() {
+    String providerId = tester.users().generateProviderId();
+    String oldLogin = tester.users().generateLogin();
+
+    // First authentication to create the user, then create a ws-client using a token
+    authenticate(oldLogin, providerId);
+    String userToken = tester.wsClient().userTokens().generate(new GenerateRequest().setLogin(oldLogin).setName("auth-token")).getToken();
+    WsClient userWsClient = tester.as(userToken, null).wsClient();
+
+    // Generate some user tokens
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token1"));
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token2"));
+    assertThat(userWsClient.userTokens().search(new org.sonarqube.ws.client.usertokens.SearchRequest()).getUserTokensList())
+      .extracting(UserTokens.SearchWsResponse.UserToken::getName)
+      .contains("token1", "token2");
+
+    // Update login during authentication, check user tokens are still there
+    String newLogin = tester.users().generateLogin();
+    authenticate(newLogin, providerId);
+
+    assertThat(userWsClient.userTokens().search(new org.sonarqube.ws.client.usertokens.SearchRequest()).getUserTokensList())
+      .extracting(UserTokens.SearchWsResponse.UserToken::getName)
+      .contains("token1", "token2");
+  }
+
   private void authenticate(String login, String providerId) {
     tester.settings().setGlobalSettings("sonar.auth.fake-base-id-provider.user", login + "," + providerId + ",fake-" + login + ",John,john@email.com");
     tester.wsClient().wsConnector().call(
index 0f448dd9a7a5651035b4ab37c17236104568e733..e4e1b1c94f8fe8e8bda988449655e7a5df0f3bee 100644 (file)
@@ -40,7 +40,8 @@ import static util.ItUtils.xooPlugin;
   NotificationsWsTest.class,
   OAuth2IdentityProviderTest.class,
   RootUserInStandaloneModeTest.class,
-  UsersPageTest.class
+  UsersPageTest.class,
+  UserTokensTest.class
 })
 public class UserSuite {
 
diff --git a/tests/src/test/java/org/sonarqube/tests/user/UserTokensTest.java b/tests/src/test/java/org/sonarqube/tests/user/UserTokensTest.java
new file mode 100644 (file)
index 0000000..10079a8
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.sonarqube.tests.user;
+
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.UserTokens.SearchWsResponse.UserToken;
+import org.sonarqube.ws.Users.CreateWsResponse.User;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.usertokens.GenerateRequest;
+import org.sonarqube.ws.client.usertokens.RevokeRequest;
+import org.sonarqube.ws.client.usertokens.SearchRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UserTokensTest {
+
+  @ClassRule
+  public static final Orchestrator orchestrator = UserSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator).disableOrganizations();
+
+  @Test
+  public void generate_and_search_for_user_tokens() {
+    User user = tester.users().generate();
+    // Generate tokens for the user
+    WsClient userWsClient = tester.as(user.getLogin()).wsClient();
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token1"));
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token2"));
+    // Generate token for another user
+    User anotherUser = tester.users().generate();
+    tester.as(anotherUser.getLogin()).wsClient().userTokens().generate(new GenerateRequest().setName("token"));
+
+    assertThat(userWsClient.userTokens().search(new SearchRequest()).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1", "token2");
+  }
+
+  @Test
+  public void revoke_user_token() {
+    User user = tester.users().generate();
+    WsClient userWsClient = tester.as(user.getLogin()).wsClient();
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token1"));
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token2"));
+    assertThat(userWsClient.userTokens().search(new SearchRequest()).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1", "token2");
+
+    userWsClient.userTokens().revoke(new RevokeRequest().setName("token2"));
+
+    assertThat(userWsClient.userTokens().search(new SearchRequest()).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1");
+  }
+
+  @Test
+  public void admin_can_generate_and_search_for_any_user_tokens() {
+    User user = tester.users().generate();
+    User admin = tester.users().generateAdministrator();
+    WsClient adminWsClient = tester.as(admin.getLogin()).wsClient();
+
+    adminWsClient.userTokens().generate(new GenerateRequest().setLogin(user.getLogin()).setName("token1"));
+    adminWsClient.userTokens().generate(new GenerateRequest().setLogin(user.getLogin()).setName("token2"));
+
+    assertThat(adminWsClient.userTokens().search(new SearchRequest().setLogin(user.getLogin())).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1", "token2");
+  }
+
+  @Test
+  public void admin_can_revoke_token_from_any_user() {
+    User user = tester.users().generate();
+    WsClient userWsClient = tester.as(user.getLogin()).wsClient();
+    User admin = tester.users().generateAdministrator();
+    WsClient adminWsClient = tester.as(admin.getLogin()).wsClient();
+
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token1"));
+    userWsClient.userTokens().generate(new GenerateRequest().setName("token2"));
+    assertThat(userWsClient.userTokens().search(new SearchRequest()).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1", "token2");
+
+    adminWsClient.userTokens().revoke(new RevokeRequest().setLogin(user.getLogin()).setName("token2"));
+
+    assertThat(userWsClient.userTokens().search(new SearchRequest()).getUserTokensList())
+      .extracting(UserToken::getName)
+      .containsExactlyInAnyOrder("token1");
+  }
+}