*/
package org.sonar.server.user;
+import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.picocontainer.Startable;
import org.sonar.api.CoreProperties;
// nothing
}
+ @Nullable
public SecurityRealm getRealm() {
return realm;
}
+ public boolean hasExternalAuthentication() {
+ return getRealm() != null;
+ }
+
private static SecurityRealm selectRealm(SecurityRealm[] realms, String realmName) {
for (SecurityRealm realm : realms) {
if (StringUtils.equals(realmName, realm.getName())) {
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
-import org.sonar.api.server.ServerSide;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.NewUserHandler;
+import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.user.GroupDto;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.util.Validation;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Random;
-
import static com.google.common.collect.Lists.newArrayList;
@ServerSide
private final DbClient dbClient;
private final UserIndexer userIndexer;
private final System2 system2;
+ private final SecurityRealmFactory realmFactory;
- public UserUpdater(NewUserNotifier newUserNotifier, Settings settings, DbClient dbClient, UserIndexer userIndexer, System2 system2) {
+ public UserUpdater(NewUserNotifier newUserNotifier, Settings settings, DbClient dbClient, UserIndexer userIndexer, System2 system2, SecurityRealmFactory realmFactory) {
this.newUserNotifier = newUserNotifier;
this.settings = settings;
this.dbClient = dbClient;
this.userIndexer = userIndexer;
this.system2 = system2;
+ this.realmFactory = realmFactory;
}
/**
String password = updateUser.password();
String passwordConfirmation = updateUser.passwordConfirmation();
if (updateUser.isPasswordChanged()) {
+ checkPasswordChangeAllowed(messages);
validatePasswords(password, passwordConfirmation, messages);
setEncryptedPassWord(password, userDto);
}
}
}
+ private void checkPasswordChangeAllowed(List<Message> messages) {
+ if (realmFactory.hasExternalAuthentication()) {
+ messages.add(Message.of("user.password_cant_be_changed_on_external_auth"));
+ }
+ }
+
private static void validatePasswords(@Nullable String password, @Nullable String passwordConfirmation, List<Message> messages) {
checkNotEmptyParam(password, PASSWORD_PARAM, messages);
checkNotEmptyParam(passwordConfirmation, PASSWORD_CONFIRMATION_PARAM, messages);
public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction("change_password")
.setDescription("Update a user's password. Authenticated users can change their own password, " +
+ "provided that the account is not linked to an external authentication system. " +
"Administer System permission is required to change another user's password.")
.setSince("5.2")
.setPost(true)
SecurityRealmFactory factory = new SecurityRealmFactory(settings, new SecurityRealm[]{realm});
factory.start();
assertThat(factory.getRealm()).isSameAs(realm);
+ assertThat(factory.hasExternalAuthentication()).isTrue();
verify(realm).init();
factory.stop();
SecurityRealmFactory factory = new SecurityRealmFactory(settings);
factory.start();
assertThat(factory.getRealm()).isNull();
+ assertThat(factory.hasExternalAuthentication()).isFalse();
}
@Test
@Mock
NewUserNotifier newUserNotifier;
+ @Mock
+ SecurityRealmFactory realmFactory;
+
@Captor
ArgumentCaptor<NewUserHandler.Context> newUserHandler;
DbClient dbClient = new DbClient(db.database(), db.myBatis(), userDao, groupDao, userGroupDao);
userIndexer = (UserIndexer) new UserIndexer(dbClient, es.client()).setEnabled(true);
userUpdater = new UserUpdater(newUserNotifier, settings, dbClient,
- userIndexer, system2);
+ userIndexer, system2, realmFactory);
}
@After
assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
}
+ @Test
+ public void fail_to_update_password_when_external_auth_is_used() {
+ db.prepareDbUnit(getClass(), "update_user.xml");
+ createDefaultGroup();
+
+ when(realmFactory.hasExternalAuthentication()).thenReturn(true);
+
+ try {
+ userUpdater.update(UpdateUser.create("marius")
+ .setPassword("password2")
+ .setPasswordConfirmation("password2"));
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.password_cant_be_changed_on_external_auth"));
+ }
+ }
+
+
@Test
public void associate_default_group_when_updating_user() {
db.prepareDbUnit(getClass(), "associate_default_groups_when_updating_user.xml");
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.core.user.UserDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.NewUserNotifier;
+import org.sonar.server.user.SecurityRealmFactory;
import org.sonar.server.user.UserUpdater;
import org.sonar.server.user.db.GroupDao;
import org.sonar.server.user.db.UserDao;
import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+@RunWith(MockitoJUnitRunner.class)
public class ChangePasswordActionTest {
static final Settings settings = new Settings().setProperty("sonar.defaultGroup", "sonar-users");
DbSession session;
+ @Mock
+ SecurityRealmFactory realmFactory;
+
@Before
public void setUp() {
dbTester.truncateTables();
userIndexer = (UserIndexer) new UserIndexer(dbClient, esTester.client()).setEnabled(true);
index = new UserIndex(esTester.client());
- tester = new WsTester(new UsersWs(new ChangePasswordAction(new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2), userSessionRule)));
+ tester = new WsTester(
+ new UsersWs(new ChangePasswordAction(new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2, realmFactory), userSessionRule)));
controller = tester.controller("api/users");
}
assertThat(newPassword).isNotEqualTo(originalPassword);
}
+ @Test(expected = BadRequestException.class)
+ public void fail_to_update_password_on_external_auth() throws Exception {
+ createUser();
+ session.clearCache();
+ when(realmFactory.hasExternalAuthentication()).thenReturn(true);
+
+ tester.newPostRequest("api/users", "change_password")
+ .setParam("login", "john")
+ .setParam("password", "Valar Morghulis")
+ .execute();
+ }
+
private void createUser() {
dbClient.userDao().insert(session, new UserDto()
.setEmail("john@email.com")
package org.sonar.server.user.ws;
+import java.util.Locale;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.NewUserNotifier;
+import org.sonar.server.user.SecurityRealmFactory;
import org.sonar.server.user.UserUpdater;
import org.sonar.server.user.db.GroupDao;
import org.sonar.server.user.db.UserDao;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.ws.WsTester;
-import java.util.Locale;
-
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
userIndexer = (UserIndexer) new UserIndexer(dbClient, esTester.client()).setEnabled(true);
index = new UserIndex(esTester.client());
tester = new WsTester(new UsersWs(new CreateAction(index,
- new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2),
+ new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2, mock(SecurityRealmFactory.class)),
i18n, userSessionRule)));
controller = tester.controller("api/users");
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.NewUserNotifier;
+import org.sonar.server.user.SecurityRealmFactory;
import org.sonar.server.user.UserUpdater;
import org.sonar.server.user.db.UserDao;
import org.sonar.server.user.index.UserDoc;
userIndexer = (UserIndexer) new UserIndexer(dbClient, esTester.client()).setEnabled(true);
index = new UserIndex(esTester.client());
tester = new WsTester(new UsersWs(new DeactivateAction(index,
- new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2), userSessionRule)));
+ new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2, mock(SecurityRealmFactory.class)), userSessionRule)));
controller = tester.controller("api/users");
}
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.NewUserNotifier;
+import org.sonar.server.user.SecurityRealmFactory;
import org.sonar.server.user.UserUpdater;
import org.sonar.server.user.db.GroupDao;
import org.sonar.server.user.db.UserDao;
userIndexer = (UserIndexer) new UserIndexer(dbClient, esTester.client()).setEnabled(true);
index = new UserIndex(esTester.client());
tester = new WsTester(new UsersWs(new UpdateAction(index,
- new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2), userSessionRule)));
+ new UserUpdater(mock(NewUserNotifier.class), settings, dbClient, userIndexer, system2, mock(SecurityRealmFactory.class)), userSessionRule)));
controller = tester.controller("api/users");
}
user.add_scm_account=Add SCM account
user.scm_account_already_used=The scm account '{0}' is already used by user(s) : '{1}'
user.login_or_email_used_as_scm_account=Login and email are automatically considered as SCM accounts
+user.password_cant_be_changed_on_external_auth=Password cannot be changed when external authentication is used
#------------------------------------------------------------------------------