Browse Source

SONAR-6949 Implements bcrypt hash for password

Extract hash mechanism into a single class LocalAuthentication
Implements SHA1 (deprecated) and bcrypt hash
Set bcrypt as default
Update the hash of a user during authentication if hash method was SHA1
tags/7.5
Eric Hartmann 6 years ago
parent
commit
7f88e7c22d
26 changed files with 476 additions and 108 deletions
  1. 1
    1
      server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql
  2. 4
    10
      server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java
  3. 1
    0
      server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml
  4. 4
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java
  5. 0
    17
      server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDtoTest.java
  6. 1
    0
      server/sonar-db-migration/build.gradle
  7. 1
    1
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java
  8. 4
    3
      server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/IncreaseCryptedPasswordSizeTest.java
  9. 1
    0
      server/sonar-server/build.gradle
  10. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java
  11. 6
    32
      server/sonar-server/src/main/java/org/sonar/server/authentication/CredentialsAuthenticator.java
  12. 200
    0
      server/sonar-server/src/main/java/org/sonar/server/authentication/LocalAuthentication.java
  13. 8
    16
      server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java
  14. 11
    6
      server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java
  15. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java
  16. 6
    1
      server/sonar-server/src/test/java/org/sonar/server/authentication/CredentialsAuthenticatorTest.java
  17. 186
    0
      server/sonar-server/src/test/java/org/sonar/server/authentication/LocalAuthenticationTest.java
  18. 2
    1
      server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java
  19. 3
    1
      server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java
  20. 8
    3
      server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterCreateTest.java
  21. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterUpdateTest.java
  22. 5
    2
      server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
  23. 3
    1
      server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java
  24. 10
    8
      server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
  25. 6
    1
      server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsTest.java
  26. 0
    1
      sonar-core/build.gradle

+ 1
- 1
server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql View File

@@ -1,4 +1,4 @@
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, IS_ROOT, ONBOARDED, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', false, false, '1418215735482', '1418215735482');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, HASH_METHOD, IS_ROOT, ONBOARDED, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, '$2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi', null, 'BCRYPT', false, false, '1418215735482', '1418215735482');
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;

INSERT INTO GROUPS(ID, ORGANIZATION_UUID, NAME, DESCRIPTION, CREATED_AT, UPDATED_AT) VALUES (1, 'AVdqnciQUUs7Zd3KPvFD', 'sonar-administrators', 'System administrators', '2011-09-26 22:27:51.0', '2011-09-26 22:27:51.0');

+ 4
- 10
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java View File

@@ -25,11 +25,8 @@ import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.codec.digest.DigestUtils;
import org.sonar.core.user.DefaultUser;

import static java.util.Objects.requireNonNull;

/**
* @since 3.2
*/
@@ -44,8 +41,11 @@ public class UserDto {
private String scmAccounts;
private String externalIdentity;
private String externalIdentityProvider;
// Hashed password that may be null in case of external authentication
private String cryptedPassword;
// Salt used for SHA1, null when bcrypt is used or for external authentication
private String salt;
// Hash method used to generate cryptedPassword, my be null in case of external authentication
private String hashMethod;
private Long createdAt;
private Long updatedAt;
@@ -192,7 +192,7 @@ public class UserDto {
return hashMethod;
}

public UserDto setHashMethod(String hashMethod) {
public UserDto setHashMethod(@Nullable String hashMethod) {
this.hashMethod = hashMethod;
return this;
}
@@ -260,12 +260,6 @@ public class UserDto {
return this;
}

public static String encryptPassword(String password, String salt) {
requireNonNull(password, "Password cannot be empty");
requireNonNull(salt, "Salt cannot be empty");
return DigestUtils.sha1Hex("--" + salt + "--" + password + "--");
}

public DefaultUser toUser() {
return new DefaultUser()
.setLogin(login)

+ 1
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml View File

@@ -221,6 +221,7 @@
onboarded = #{user.onboarded, jdbcType=BOOLEAN},
salt = #{user.salt, jdbcType=VARCHAR},
crypted_password = #{user.cryptedPassword, jdbcType=BIGINT},
hash_method = #{user.hashMethod, jdbcType=VARCHAR},
updated_at = #{now, jdbcType=BIGINT},
homepage_type = #{user.homepageType, jdbcType=VARCHAR},
homepage_parameter = #{user.homepageParameter, jdbcType=VARCHAR}

+ 4
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java View File

@@ -319,6 +319,7 @@ public class UserDaoTest {
.setOnboarded(true)
.setSalt("1234")
.setCryptedPassword("abcd")
.setHashMethod("SHA1")
.setExternalIdentity("johngithub")
.setExternalIdentityProvider("github")
.setLocal(true)
@@ -340,6 +341,7 @@ public class UserDaoTest {
assertThat(user.getScmAccounts()).isEqualTo(",jo.hn,john2,");
assertThat(user.getSalt()).isEqualTo("1234");
assertThat(user.getCryptedPassword()).isEqualTo("abcd");
assertThat(user.getHashMethod()).isEqualTo("SHA1");
assertThat(user.getExternalIdentity()).isEqualTo("johngithub");
assertThat(user.getExternalIdentityProvider()).isEqualTo("github");
assertThat(user.isLocal()).isTrue();
@@ -368,6 +370,7 @@ public class UserDaoTest {
.setOnboarded(true)
.setSalt("12345")
.setCryptedPassword("abcde")
.setHashMethod("BCRYPT")
.setExternalIdentity("johngithub")
.setExternalIdentityProvider("github")
.setLocal(false)
@@ -386,6 +389,7 @@ public class UserDaoTest {
assertThat(reloaded.getScmAccounts()).isEqualTo(",jo.hn,john2,johndoo,");
assertThat(reloaded.getSalt()).isEqualTo("12345");
assertThat(reloaded.getCryptedPassword()).isEqualTo("abcde");
assertThat(reloaded.getHashMethod()).isEqualTo("BCRYPT");
assertThat(reloaded.getExternalIdentity()).isEqualTo("johngithub");
assertThat(reloaded.getExternalIdentityProvider()).isEqualTo("github");
assertThat(reloaded.isLocal()).isFalse();

+ 0
- 17
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDtoTest.java View File

@@ -46,21 +46,4 @@ public class UserDtoTest {
assertThat(UserDto.decodeScmAccounts("\nfoo\n")).containsOnly("foo");
assertThat(UserDto.decodeScmAccounts("\nfoo\nbar\n")).containsOnly("foo", "bar");
}

@Test
public void encrypt_password() {
assertThat(UserDto.encryptPassword("PASSWORD", "0242b0b4c0a93ddfe09dd886de50bc25ba000b51")).isEqualTo("540e4fc4be4e047db995bc76d18374a5b5db08cc");
}

@Test
public void fail_to_encrypt_password_when_password_is_null() {
expectedException.expect(NullPointerException.class);
UserDto.encryptPassword(null, "salt");
}

@Test
public void fail_to_encrypt_password_when_salt_is_null() {
expectedException.expect(NullPointerException.class);
UserDto.encryptPassword("password", null);
}
}

+ 1
- 0
server/sonar-db-migration/build.gradle View File

@@ -18,6 +18,7 @@ dependencies {
testCompile 'org.assertj:assertj-core'
testCompile 'org.dbunit:dbunit'
testCompile 'org.mockito:mockito-core'
testCompile 'org.mindrot:jbcrypt'
testCompile project(':sonar-testing-harness')
testCompile project(':server:sonar-db-core').sourceSets.test.output


+ 1
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v72/DbVersion72.java View File

@@ -27,7 +27,7 @@ public class DbVersion72 implements DbVersion {
@Override
public void addSteps(MigrationStepRegistry registry) {
registry
.add(2100, "Increase size of CRYPTED_PASSWORD", IncreaseCryptedPasswordSize.class)
.add(2100, "Increase size of USERS.CRYPTED_PASSWORD", IncreaseCryptedPasswordSize.class)
.add(2101, "Add HASH_METHOD to table users", AddHashMethodToUsersTable.class)
.add(2102, "Populate HASH_METHOD on table users", PopulateHashMethodOnUsers.class)
;

+ 4
- 3
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v72/IncreaseCryptedPasswordSizeTest.java View File

@@ -1,4 +1,4 @@
package org.sonar.server.platform.db.migration.version.v72;/*
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
@@ -17,12 +17,12 @@ package org.sonar.server.platform.db.migration.version.v72;/*
* 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.mindrot.jbcrypt.BCrypt;
import org.sonar.db.CoreDbTester;

import static org.assertj.core.api.Assertions.assertThat;
@@ -53,9 +53,10 @@ public class IncreaseCryptedPasswordSizeTest {
}

private void insertRow() {
// bcrypt hash is 60 characters
db.executeInsert(
"USERS",
"CRYPTED_PASSWORD", BCrypt.hashpw("a", BCrypt.gensalt()),
"CRYPTED_PASSWORD", "$2a$10$8tscphgcElKF5vOBer4H.OVfLKpPIH74hK.rxyhOP5HVyZHyfgRGy",
"IS_ROOT", false,
"ONBOARDED", false);
}

+ 1
- 0
server/sonar-server/build.gradle View File

@@ -45,6 +45,7 @@ dependencies {
compile 'org.slf4j:jul-to-slf4j'
compile 'org.slf4j:slf4j-api'
compile 'org.sonarsource.update-center:sonar-update-center-common'
compile 'org.mindrot:jbcrypt'

compile project(':server:sonar-db-dao')
compile project(':server:sonar-db-migration')

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/authentication/AuthenticationModule.java View File

@@ -47,6 +47,7 @@ public class AuthenticationModule extends Module {
LoginAction.class,
LogoutAction.class,
CredentialsAuthenticator.class,
LocalAuthentication.class,
RealmAuthenticator.class,
BasicAuthenticator.class,
ValidateAction.class,

+ 6
- 32
server/sonar-server/src/main/java/org/sonar/server/authentication/CredentialsAuthenticator.java View File

@@ -20,8 +20,6 @@
package org.sonar.server.authentication;

import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -29,7 +27,6 @@ import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;

import static org.sonar.db.user.UserDto.encryptPassword;
import static org.sonar.server.authentication.event.AuthenticationEvent.Method;
import static org.sonar.server.authentication.event.AuthenticationEvent.Source;

@@ -38,11 +35,14 @@ public class CredentialsAuthenticator {
private final DbClient dbClient;
private final RealmAuthenticator externalAuthenticator;
private final AuthenticationEvent authenticationEvent;
private final LocalAuthentication localAuthentication;

public CredentialsAuthenticator(DbClient dbClient, RealmAuthenticator externalAuthenticator, AuthenticationEvent authenticationEvent) {
public CredentialsAuthenticator(DbClient dbClient, RealmAuthenticator externalAuthenticator, AuthenticationEvent authenticationEvent,
LocalAuthentication localAuthentication) {
this.dbClient = dbClient;
this.externalAuthenticator = externalAuthenticator;
this.authenticationEvent = authenticationEvent;
this.localAuthentication = localAuthentication;
}

public UserDto authenticate(String userLogin, String userPassword, HttpServletRequest request, Method method) {
@@ -54,9 +54,9 @@ public class CredentialsAuthenticator {
private UserDto authenticate(DbSession dbSession, String userLogin, String userPassword, HttpServletRequest request, Method method) {
UserDto localUser = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin);
if (localUser != null && localUser.isLocal()) {
UserDto userDto = authenticateFromDb(localUser, userPassword, method);
localAuthentication.authenticate(dbSession, localUser, userPassword, method);
authenticationEvent.loginSuccess(request, userLogin, Source.local(method));
return userDto;
return localUser;
}
Optional<UserDto> externalUser = externalAuthenticator.authenticate(userLogin, userPassword, request, method);
if (externalUser.isPresent()) {
@@ -68,30 +68,4 @@ public class CredentialsAuthenticator {
.setMessage(localUser != null && !localUser.isLocal() ? "User is not local" : "No active user for login")
.build();
}

private static UserDto authenticateFromDb(UserDto userDto, String userPassword, Method method) {
String cryptedPassword = userDto.getCryptedPassword();
String salt = userDto.getSalt();
String failureCause = checkPassword(cryptedPassword, salt, userPassword);
if (failureCause == null) {
return userDto;
}
throw AuthenticationException.newBuilder()
.setSource(Source.local(method))
.setLogin(userDto.getLogin())
.setMessage(failureCause)
.build();
}

@CheckForNull
private static String checkPassword(@Nullable String cryptedPassword, @Nullable String salt, String userPassword) {
if (cryptedPassword == null) {
return "null password in DB";
} else if (salt == null) {
return "null salt";
} else if (!cryptedPassword.equals(encryptPassword(userPassword, salt))) {
return "wrong password";
}
return null;
}
}

+ 200
- 0
server/sonar-server/src/main/java/org/sonar/server/authentication/LocalAuthentication.java View File

@@ -0,0 +1,200 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.authentication;

import java.security.SecureRandom;
import org.apache.commons.codec.digest.DigestUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent.Method;
import org.sonar.server.authentication.event.AuthenticationEvent.Source;
import org.sonar.server.authentication.event.AuthenticationException;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

/**
* This class is responsible of handling local authentication
*/
public class LocalAuthentication {

private final DbClient dbClient;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
// The default hash method that must be used is BCRYPT
private static final HashMethod DEFAULT = HashMethod.BCRYPT;

public LocalAuthentication(DbClient dbClient) {
this.dbClient = dbClient;
}

/**
* This method authenticate a user with his password against the value stored in user.
* If authentication failed an AuthenticationException will be thrown containing the failure message.
* If the password must be updated because an old algorithm is used, the UserDto is updated but the session
* is not committed
*/
public void authenticate(DbSession session, UserDto user, String password, Method method) {
if (user.getHashMethod() == null) {
throw AuthenticationException.newBuilder()
.setSource(Source.local(method))
.setLogin(user.getLogin())
.setMessage("null hash method")
.build();
}

HashMethod hashMethod;
try {
hashMethod = HashMethod.valueOf(user.getHashMethod());
} catch (IllegalArgumentException ex) {
throw AuthenticationException.newBuilder()
.setSource(Source.local(method))
.setLogin(user.getLogin())
.setMessage(format("Unknown hash method [%s]", user.getHashMethod()))
.build();
}

AuthenticationResult result = hashMethod.checkCredentials(user, password);
if (!result.isSuccessful()) {
throw AuthenticationException.newBuilder()
.setSource(Source.local(method))
.setLogin(user.getLogin())
.setMessage(result.getFailureMessage())
.build();
}

// Upgrade the password if it's an old hashMethod
if (hashMethod != DEFAULT) {
DEFAULT.storeHashPassword(user, password);
dbClient.userDao().update(session, user);
}
}

/**
* Method used to store the password as a hash in database.
* The crypted_password, salt and hash_method are set
*/
public void storeHashPassword(UserDto user, String password) {
DEFAULT.storeHashPassword(user, password);
}

public enum HashMethod implements HashFunction {
SHA1(new Sha1Function()), BCRYPT(new BcryptFunction());

private HashFunction hashFunction;

HashMethod(HashFunction hashFunction) {
this.hashFunction = hashFunction;
}

@Override
public AuthenticationResult checkCredentials(UserDto user, String password) {
return hashFunction.checkCredentials(user, password);
}

@Override
public void storeHashPassword(UserDto user, String password) {
hashFunction.storeHashPassword(user, password);
}
}

private static class AuthenticationResult {
private final boolean successful;
private final String failureMessage;

private AuthenticationResult(boolean successful, String failureMessage) {
checkArgument((successful && failureMessage.isEmpty()) || (!successful && !failureMessage.isEmpty()), "Incorrect parameters");
this.successful = successful;
this.failureMessage = failureMessage;
}

public boolean isSuccessful() {
return successful;
}

public String getFailureMessage() {
return failureMessage;
}
}

public interface HashFunction {
AuthenticationResult checkCredentials(UserDto user, String password);
void storeHashPassword(UserDto user, String password);
}

/**
* Implementation of deprecated SHA1 hash function
*/
private static final class Sha1Function implements HashFunction {
@Override
public AuthenticationResult checkCredentials(UserDto user, String password) {
if (user.getCryptedPassword() == null) {
return new AuthenticationResult(false, "null password in DB");
}
if (user.getSalt() == null) {
return new AuthenticationResult(false, "null salt");
}
if (!user.getCryptedPassword().equals(hash(user.getSalt(), password))) {
return new AuthenticationResult(false, "wrong password");
}
return new AuthenticationResult(true, "");
}

@Override
public void storeHashPassword(UserDto user, String password) {
requireNonNull(password, "Password cannot be null");
byte[] saltRandom = new byte[20];
SECURE_RANDOM.nextBytes(saltRandom);
String salt = DigestUtils.sha1Hex(saltRandom);

user.setHashMethod(HashMethod.SHA1.name())
.setCryptedPassword(hash(salt, password))
.setSalt(salt);
}

private String hash(String salt, String password) {
return DigestUtils.sha1Hex("--" + salt + "--" + password + "--");
}
}

/**
* Implementation of bcrypt hash function
*/
private static final class BcryptFunction implements HashFunction {
@Override
public AuthenticationResult checkCredentials(UserDto user, String password) {
if (!BCrypt.checkpw(password, user.getCryptedPassword())) {
return new AuthenticationResult(false, "wrong password");
}
return new AuthenticationResult(true, "");
}

@Override
public void storeHashPassword(UserDto user, String password) {
requireNonNull(password, "Password cannot be null");
user.setHashMethod(HashMethod.BCRYPT.name())
.setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)))
.setSalt(null);
}
}
}

+ 8
- 16
server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java View File

@@ -27,11 +27,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.apache.commons.codec.digest.DigestUtils;
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.NewUserHandler;
import org.sonar.api.server.ServerSide;
@@ -41,6 +39,7 @@ import org.sonar.db.organization.OrganizationMemberDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserGroupDto;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.OrganizationCreation;
import org.sonar.server.organization.OrganizationFlags;
@@ -56,7 +55,6 @@ import static java.util.Arrays.stream;
import static java.util.stream.Stream.concat;
import static org.sonar.core.config.CorePropertyDefinitions.ONBOARDING_TUTORIAL_SHOW_TO_NEW_USERS;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.db.user.UserDto.encryptPassword;
import static org.sonar.server.ws.WsUtils.checkFound;
import static org.sonar.server.ws.WsUtils.checkRequest;

@@ -83,9 +81,11 @@ public class UserUpdater {
private final OrganizationCreation organizationCreation;
private final DefaultGroupFinder defaultGroupFinder;
private final Configuration config;
private final LocalAuthentication localAuthentication;

public UserUpdater(NewUserNotifier newUserNotifier, DbClient dbClient, UserIndexer userIndexer, OrganizationFlags organizationFlags,
DefaultOrganizationProvider defaultOrganizationProvider, OrganizationCreation organizationCreation, DefaultGroupFinder defaultGroupFinder, Configuration config) {
DefaultOrganizationProvider defaultOrganizationProvider, OrganizationCreation organizationCreation, DefaultGroupFinder defaultGroupFinder, Configuration config,
LocalAuthentication localAuthentication) {
this.newUserNotifier = newUserNotifier;
this.dbClient = dbClient;
this.userIndexer = userIndexer;
@@ -94,6 +94,7 @@ public class UserUpdater {
this.organizationCreation = organizationCreation;
this.defaultGroupFinder = defaultGroupFinder;
this.config = config;
this.localAuthentication = localAuthentication;
}

public UserDto createAndCommit(DbSession dbSession, NewUser newUser, Consumer<UserDto> beforeCommit, UserDto... otherUsersToIndex) {
@@ -165,7 +166,7 @@ public class UserUpdater {

String password = newUser.password();
if (password != null && validatePasswords(password, messages)) {
setEncryptedPassword(password, userDto);
localAuthentication.storeHashPassword(userDto, password);
}

List<String> scmAccounts = sanitizeScmAccounts(newUser.scmAccounts());
@@ -218,10 +219,10 @@ public class UserUpdater {
return false;
}

private static boolean updatePassword(UpdateUser updateUser, UserDto userDto, List<String> messages) {
private boolean updatePassword(UpdateUser updateUser, UserDto userDto, List<String> messages) {
String password = updateUser.password();
if (updateUser.isPasswordChanged() && validatePasswords(password, messages) && checkPasswordChangeAllowed(userDto, messages)) {
setEncryptedPassword(password, userDto);
localAuthentication.storeHashPassword(userDto, password);
return true;
}
return false;
@@ -377,15 +378,6 @@ public class UserUpdater {
dbClient.userDao().update(dbSession, dto);
}

private static void setEncryptedPassword(String password, UserDto userDto) {
Random random = new SecureRandom();
byte[] salt = new byte[32];
random.nextBytes(salt);
String saltHex = DigestUtils.sha1Hex(salt);
userDto.setSalt(saltHex);
userDto.setCryptedPassword(encryptPassword(password, saltHex));
}

private void notifyNewUser(String login, String name, @Nullable String email) {
newUserNotifier.onNewUser(NewUserHandler.Context.builder()
.setLogin(login)

+ 11
- 6
server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java View File

@@ -25,13 +25,13 @@ 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.server.authentication.LocalAuthentication;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;
import org.sonar.server.user.UpdateUser;
import org.sonar.server.user.UserSession;
import org.sonar.server.user.UserUpdater;

import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.db.user.UserDto.encryptPassword;

public class ChangePasswordAction implements UsersWsAction {

private static final String PARAM_LOGIN = "login";
@@ -41,11 +41,13 @@ public class ChangePasswordAction implements UsersWsAction {
private final DbClient dbClient;
private final UserUpdater userUpdater;
private final UserSession userSession;
private final LocalAuthentication localAuthentication;

public ChangePasswordAction(DbClient dbClient, UserUpdater userUpdater, UserSession userSession) {
public ChangePasswordAction(DbClient dbClient, UserUpdater userUpdater, UserSession userSession, LocalAuthentication localAuthentication) {
this.dbClient = dbClient;
this.userUpdater = userUpdater;
this.userSession = userSession;
this.localAuthentication = localAuthentication;
}

@Override
@@ -97,7 +99,10 @@ public class ChangePasswordAction implements UsersWsAction {

private void checkCurrentPassword(DbSession dbSession, String login, String password) {
UserDto user = dbClient.userDao().selectOrFailByLogin(dbSession, login);
String cryptedPassword = encryptPassword(password, user.getSalt());
checkArgument(cryptedPassword.equals(user.getCryptedPassword()), "Incorrect password");
try {
localAuthentication.authenticate(dbSession, user, password, AuthenticationEvent.Method.BASIC);
} catch (AuthenticationException ex) {
throw new IllegalArgumentException("Incorrect password");
}
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/authentication/AuthenticationModuleTest.java View File

@@ -30,7 +30,7 @@ public class AuthenticationModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new AuthenticationModule().configure(container);
assertThat(container.size()).isEqualTo(2 + 22);
assertThat(container.size()).isEqualTo(2 + 23);
}

}

+ 6
- 1
server/sonar-server/src/test/java/org/sonar/server/authentication/CredentialsAuthenticatorTest.java View File

@@ -62,14 +62,16 @@ public class CredentialsAuthenticatorTest {
private RealmAuthenticator externalAuthenticator = mock(RealmAuthenticator.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
private AuthenticationEvent authenticationEvent = mock(AuthenticationEvent.class);
private LocalAuthentication localAuthentication = new LocalAuthentication(dbClient);

private CredentialsAuthenticator underTest = new CredentialsAuthenticator(dbClient, externalAuthenticator, authenticationEvent);
private CredentialsAuthenticator underTest = new CredentialsAuthenticator(dbClient, externalAuthenticator, authenticationEvent, localAuthentication);

@Test
public void authenticate_local_user() {
insertUser(newUserDto()
.setLogin(LOGIN)
.setCryptedPassword(CRYPTED_PASSWORD)
.setHashMethod(LocalAuthentication.HashMethod.SHA1.name())
.setSalt(SALT)
.setLocal(true));

@@ -84,6 +86,7 @@ public class CredentialsAuthenticatorTest {
.setLogin(LOGIN)
.setCryptedPassword("Wrong password")
.setSalt("Wrong salt")
.setHashMethod(LocalAuthentication.HashMethod.SHA1.name())
.setLocal(true));

expectedException.expect(authenticationException().from(Source.local(BASIC)).withLogin(LOGIN).andNoPublicMessage());
@@ -130,6 +133,7 @@ public class CredentialsAuthenticatorTest {
.setLogin(LOGIN)
.setCryptedPassword(null)
.setSalt(SALT)
.setHashMethod(LocalAuthentication.HashMethod.SHA1.name())
.setLocal(true));

expectedException.expect(authenticationException().from(Source.local(BASIC)).withLogin(LOGIN).andNoPublicMessage());
@@ -147,6 +151,7 @@ public class CredentialsAuthenticatorTest {
.setLogin(LOGIN)
.setCryptedPassword(CRYPTED_PASSWORD)
.setSalt(null)
.setHashMethod(LocalAuthentication.HashMethod.SHA1.name())
.setLocal(true));

expectedException.expect(authenticationException().from(Source.local(BASIC_TOKEN)).withLogin(LOGIN).andNoPublicMessage());

+ 186
- 0
server/sonar-server/src/test/java/org/sonar/server/authentication/LocalAuthenticationTest.java View File

@@ -0,0 +1,186 @@
/*
* SonarQube
* Copyright (C) 2009-2018 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package org.sonar.server.authentication;

import java.util.Optional;
import java.util.Random;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mindrot.jbcrypt.BCrypt;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.event.AuthenticationEvent;
import org.sonar.server.authentication.event.AuthenticationException;

import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.server.authentication.LocalAuthentication.HashMethod.BCRYPT;
import static org.sonar.server.authentication.LocalAuthentication.HashMethod.SHA1;

public class LocalAuthenticationTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public DbTester db = DbTester.create();

private static final Random RANDOM = new Random();

private LocalAuthentication underTest = new LocalAuthentication(db.getDbClient());

@Test
public void incorrect_hash_should_throw_AuthenticationException() {
UserDto user = new UserDto()
.setHashMethod("ALGON2");

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("Unknown hash method [ALGON2]");

underTest.authenticate(db.getSession(), user, "whatever", AuthenticationEvent.Method.BASIC);
}

@Test
public void null_hash_should_throw_AuthenticationException() {
UserDto user = new UserDto();

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("null hash method");

underTest.authenticate(db.getSession(), user, "whatever", AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_bcrypt_with_correct_password_should_work() {
String password = randomAlphanumeric(60);

UserDto user = new UserDto()
.setHashMethod(BCRYPT.name())
.setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)));

underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_sha1_with_correct_password_should_work() {
String password = randomAlphanumeric(60);

byte[] saltRandom = new byte[20];
RANDOM.nextBytes(saltRandom);
String salt = DigestUtils.sha1Hex(saltRandom);

UserDto user = new UserDto()
.setHashMethod(SHA1.name())
.setCryptedPassword(DigestUtils.sha1Hex("--" + salt + "--" + password + "--"))
.setSalt(salt);

underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_sha1_with_incorrect_password_should_throw_AuthenticationException() {
String password = randomAlphanumeric(60);

byte[] saltRandom = new byte[20];
RANDOM.nextBytes(saltRandom);
String salt = DigestUtils.sha1Hex(saltRandom);

UserDto user = new UserDto()
.setHashMethod(SHA1.name())
.setCryptedPassword(DigestUtils.sha1Hex("--" + salt + "--" + password + "--"))
.setSalt(salt);

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("wrong password");

underTest.authenticate(db.getSession(), user, "WHATEVER", AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_sha1_with_empty_password_should_throw_AuthenticationException() {
byte[] saltRandom = new byte[20];
RANDOM.nextBytes(saltRandom);
String salt = DigestUtils.sha1Hex(saltRandom);

UserDto user = new UserDto()
.setHashMethod(SHA1.name())
.setSalt(salt);

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("null password in DB");

underTest.authenticate(db.getSession(), user, "WHATEVER", AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_sha1_with_empty_salt_should_throw_AuthenticationException() {
String password = randomAlphanumeric(60);

UserDto user = new UserDto()
.setHashMethod(SHA1.name())
.setCryptedPassword(DigestUtils.sha1Hex("--0242b0b4c0a93ddfe09dd886de50bc25ba000b51--" + password + "--"));

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("null salt");

underTest.authenticate(db.getSession(), user, "WHATEVER", AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_with_bcrypt_with_incorrect_password_should_throw_AuthenticationException() {
String password = randomAlphanumeric(60);

UserDto user = new UserDto()
.setHashMethod(BCRYPT.name())
.setCryptedPassword(BCrypt.hashpw(password, BCrypt.gensalt(12)));

expectedException.expect(AuthenticationException.class);
expectedException.expectMessage("wrong password");

underTest.authenticate(db.getSession(), user, "WHATEVER", AuthenticationEvent.Method.BASIC);
}

@Test
public void authentication_upgrade_hash_function_when_SHA1_was_used() {
String password = randomAlphanumeric(60);

byte[] saltRandom = new byte[20];
RANDOM.nextBytes(saltRandom);
String salt = DigestUtils.sha1Hex(saltRandom);

UserDto user = new UserDto()
.setLogin("myself")
.setHashMethod(SHA1.name())
.setCryptedPassword(DigestUtils.sha1Hex("--" + salt + "--" + password + "--"))
.setSalt(salt);
db.users().insertUser(user);

underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);

Optional<UserDto> myself = db.users().selectUserByLogin("myself");
assertThat(myself).isPresent();
assertThat(myself.get().getHashMethod()).isEqualTo(BCRYPT.name());
assertThat(myself.get().getSalt()).isNull();

// authentication must work with upgraded hash method
underTest.authenticate(db.getSession(), user, password, AuthenticationEvent.Method.BASIC);
}
}

+ 2
- 1
server/sonar-server/src/test/java/org/sonar/server/authentication/SsoAuthenticatorTest.java View File

@@ -100,12 +100,13 @@ public class SsoAuthenticatorTest {
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());

private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
private UserIdentityAuthenticator userIdentityAuthenticator = new UserIdentityAuthenticator(
db.getDbClient(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider, organizationCreation,
new DefaultGroupFinder(db.getDbClient()), settings.asConfig()),
new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
defaultOrganizationProvider, organizationFlags, new DefaultGroupFinder(db.getDbClient()));

private HttpServletResponse response = mock(HttpServletResponse.class);

+ 3
- 1
server/sonar-server/src/test/java/org/sonar/server/authentication/UserIdentityAuthenticatorTest.java View File

@@ -84,6 +84,7 @@ public class UserIdentityAuthenticatorTest {
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());
private UserUpdater userUpdater = new UserUpdater(
mock(NewUserNotifier.class),
db.getDbClient(),
@@ -92,7 +93,8 @@ public class UserIdentityAuthenticatorTest {
defaultOrganizationProvider,
organizationCreation,
new DefaultGroupFinder(db.getDbClient()),
settings.asConfig());
settings.asConfig(),
localAuthentication);

private UserIdentityAuthenticator underTest = new UserIdentityAuthenticator(db.getDbClient(), userUpdater, defaultOrganizationProvider, organizationFlags,
new DefaultGroupFinder(db.getDbClient()));

+ 8
- 3
server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterCreateTest.java View File

@@ -38,6 +38,8 @@ import org.sonar.db.DbTester;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.GroupTesting;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.authentication.LocalAuthentication.HashMethod;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.organization.DefaultOrganizationProvider;
@@ -86,8 +88,9 @@ public class UserUpdaterCreateTest {
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private MapSettings settings = new MapSettings();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());
private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, organizationFlags, defaultOrganizationProvider, organizationCreation,
new DefaultGroupFinder(dbClient), settings.asConfig());
new DefaultGroupFinder(dbClient), settings.asConfig(), localAuthentication);

@Test
public void create_user() {
@@ -110,7 +113,8 @@ public class UserUpdaterCreateTest {
assertThat(dto.isActive()).isTrue();
assertThat(dto.isLocal()).isTrue();

assertThat(dto.getSalt()).isNotNull();
assertThat(dto.getSalt()).isNull();
assertThat(dto.getHashMethod()).isEqualTo(HashMethod.BCRYPT.name());
assertThat(dto.getCryptedPassword()).isNotNull();
assertThat(dto.getCreatedAt())
.isPositive()
@@ -618,7 +622,8 @@ public class UserUpdaterCreateTest {
assertThat(dto.getScmAccounts()).isNull();
assertThat(dto.isLocal()).isTrue();

assertThat(dto.getSalt()).isNotNull().isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
assertThat(dto.getSalt()).isNull();
assertThat(dto.getHashMethod()).isEqualTo(HashMethod.BCRYPT.name());
assertThat(dto.getCryptedPassword()).isNotNull().isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
assertThat(dto.getCreatedAt()).isEqualTo(user.getCreatedAt());
assertThat(dto.getUpdatedAt()).isGreaterThan(user.getCreatedAt());

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterUpdateTest.java View File

@@ -36,6 +36,7 @@ import org.sonar.db.DbTester;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTesting;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.organization.DefaultOrganizationProvider;
@@ -71,15 +72,15 @@ public class UserUpdaterUpdateTest {

private DbClient dbClient = db.getDbClient();
private NewUserNotifier newUserNotifier = mock(NewUserNotifier.class);
private ArgumentCaptor<NewUserHandler.Context> newUserHandler = ArgumentCaptor.forClass(NewUserHandler.Context.class);
private DbSession session = db.getSession();
private UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private MapSettings settings = new MapSettings();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());
private UserUpdater underTest = new UserUpdater(newUserNotifier, dbClient, userIndexer, organizationFlags, defaultOrganizationProvider, organizationCreation,
new DefaultGroupFinder(dbClient), settings.asConfig());
new DefaultGroupFinder(dbClient), settings.asConfig(), localAuthentication);

@Test
public void update_user() {

+ 5
- 2
server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java View File

@@ -25,6 +25,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.db.DbTester;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
@@ -56,15 +57,17 @@ public class ChangePasswordActionTest {
public UserSessionRule userSessionRule = UserSessionRule.standalone().logIn();

private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());

private UserUpdater userUpdater = new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), new UserIndexer(db.getDbClient(), es.client()),
organizationFlags,
TestDefaultOrganizationProvider.from(db),
mock(OrganizationCreation.class),
new DefaultGroupFinder(db.getDbClient()),
new MapSettings().asConfig());
new MapSettings().asConfig(),
localAuthentication);

private WsTester tester = new WsTester(new UsersWs(new ChangePasswordAction(db.getDbClient(), userUpdater, userSessionRule)));
private WsTester tester = new WsTester(new UsersWs(new ChangePasswordAction(db.getDbClient(), userUpdater, userSessionRule, localAuthentication)));

@Before
public void setUp() {

+ 3
- 1
server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java View File

@@ -34,6 +34,7 @@ import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.organization.DefaultOrganizationProvider;
@@ -82,10 +83,11 @@ public class CreateActionTest {
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private OrganizationCreation organizationCreation = mock(OrganizationCreation.class);
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());
private WsActionTester tester = new WsActionTester(new CreateAction(
db.getDbClient(),
new UserUpdater(mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider,
organizationCreation, new DefaultGroupFinder(db.getDbClient()), settings.asConfig()),
organizationCreation, new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
userSessionRule));

@Before

+ 10
- 8
server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java View File

@@ -30,6 +30,7 @@ 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.authentication.LocalAuthentication;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -70,10 +71,11 @@ public class UpdateActionTest {
private UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());

private WsActionTester ws = new WsActionTester(new UpdateAction(
new UserUpdater(mock(NewUserNotifier.class), dbClient, userIndexer, organizationFlags, defaultOrganizationProvider, ORGANIZATION_CREATION_NOT_USED_FOR_UPDATE,
new DefaultGroupFinder(db.getDbClient()), settings.asConfig()), userSession, new UserJsonWriter(userSession), dbClient));
new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication), userSession, new UserJsonWriter(userSession), dbClient));

@Before
public void setUp() {
@@ -81,7 +83,7 @@ public class UpdateActionTest {
}

@Test
public void update_user() throws Exception {
public void update_user() {
createUser();

ws.newRequest()
@@ -94,7 +96,7 @@ public class UpdateActionTest {
}

@Test
public void update_only_name() throws Exception {
public void update_only_name() {
createUser();

ws.newRequest()
@@ -105,7 +107,7 @@ public class UpdateActionTest {
}

@Test
public void update_only_email() throws Exception {
public void update_only_email() {
createUser();

ws.newRequest()
@@ -116,7 +118,7 @@ public class UpdateActionTest {
}

@Test
public void blank_email_is_updated_to_null() throws Exception {
public void blank_email_is_updated_to_null() {
createUser();

ws.newRequest()
@@ -143,7 +145,7 @@ public class UpdateActionTest {
}

@Test
public void update_only_scm_accounts() throws Exception {
public void update_only_scm_accounts() {
createUser();

ws.newRequest()
@@ -196,7 +198,7 @@ public class UpdateActionTest {
}

@Test
public void update_only_scm_accounts_with_deprecated_scmAccounts_parameter() throws Exception {
public void update_only_scm_accounts_with_deprecated_scmAccounts_parameter() {
createUser();

ws.newRequest()
@@ -210,7 +212,7 @@ public class UpdateActionTest {
}

@Test
public void update_only_scm_accounts_with_deprecated_scm_accounts_parameter() throws Exception {
public void update_only_scm_accounts_with_deprecated_scm_accounts_parameter() {
createUser();

ws.newRequest()

+ 6
- 1
server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsTest.java View File

@@ -24,6 +24,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.server.authentication.LocalAuthentication;
import org.sonar.server.issue.ws.AvatarResolver;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.UserUpdater;
@@ -36,15 +38,18 @@ import static org.mockito.Mockito.mock;
public class UsersWsTest {
@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create();

private WebService.Controller controller;
private LocalAuthentication localAuthentication = new LocalAuthentication(db.getDbClient());

@Before
public void setUp() {
WsTester tester = new WsTester(new UsersWs(
new CreateAction(mock(DbClient.class), mock(UserUpdater.class), userSessionRule),
new UpdateAction(mock(UserUpdater.class), userSessionRule, mock(UserJsonWriter.class), mock(DbClient.class)),
new ChangePasswordAction(mock(DbClient.class), mock(UserUpdater.class), userSessionRule),
new ChangePasswordAction(mock(DbClient.class), mock(UserUpdater.class), userSessionRule, localAuthentication),
new SearchAction(userSessionRule, mock(UserIndex.class), mock(DbClient.class), mock(AvatarResolver.class))));
controller = tester.controller("api/users");
}

+ 0
- 1
sonar-core/build.gradle View File

@@ -20,7 +20,6 @@ dependencies {
compile 'org.picocontainer:picocontainer'
compile 'org.slf4j:slf4j-api'
compile 'org.sonarsource.update-center:sonar-update-center-common'
compile 'org.mindrot:jbcrypt'
compile project(path: ':sonar-plugin-api', configuration: 'shadow')

compileOnly 'com.google.code.findbugs:jsr305'

Loading…
Cancel
Save