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 SHA1tags/7.5
@@ -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'); |
@@ -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) |
@@ -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} |
@@ -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(); |
@@ -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); | |||
} | |||
} |
@@ -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 | |||
@@ -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) | |||
; |
@@ -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); | |||
} |
@@ -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') |
@@ -47,6 +47,7 @@ public class AuthenticationModule extends Module { | |||
LoginAction.class, | |||
LogoutAction.class, | |||
CredentialsAuthenticator.class, | |||
LocalAuthentication.class, | |||
RealmAuthenticator.class, | |||
BasicAuthenticator.class, | |||
ValidateAction.class, |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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) |
@@ -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"); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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()); |
@@ -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); | |||
} | |||
} |
@@ -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); |
@@ -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())); |
@@ -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()); |
@@ -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() { |
@@ -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() { |
@@ -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 |
@@ -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() |
@@ -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"); | |||
} |
@@ -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' |