Skip to content

Commit

Permalink
SONAR-6468 New WS to change a user's password
Browse files Browse the repository at this point in the history
  • Loading branch information
jblievremont committed May 7, 2015
1 parent 974af97 commit 7a26b1d
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ void startLevel4Components(ComponentContainer pico) {
pico.addSingleton(org.sonar.server.user.ws.CreateAction.class);
pico.addSingleton(org.sonar.server.user.ws.UpdateAction.class);
pico.addSingleton(org.sonar.server.user.ws.DeactivateAction.class);
pico.addSingleton(org.sonar.server.user.ws.ChangePasswordAction.class);
pico.addSingleton(org.sonar.server.user.ws.CurrentUserAction.class);
pico.addSingleton(org.sonar.server.user.ws.SearchAction.class);
pico.addSingleton(org.sonar.server.issue.ws.AuthorsAction.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.user.ws;

import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.server.user.UpdateUser;
import org.sonar.server.user.UserSession;
import org.sonar.server.user.UserUpdater;

public class ChangePasswordAction implements BaseUsersWsAction {

private static final String PARAM_LOGIN = "login";
private static final String PARAM_PASSWORD = "password";
private static final String PARAM_PASSWORD_CONFIRMATION = "password_confirmation";

private final UserUpdater userUpdater;

public ChangePasswordAction(UserUpdater userUpdater) {
this.userUpdater = userUpdater;
}

@Override
public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction("change_password")
.setDescription("Update a user's password. Requires Administer System permission.")
.setSince("5.2")
.setPost(true)
.setHandler(this);

action.createParam(PARAM_LOGIN)
.setDescription("User login")
.setRequired(true)
.setExampleValue("myuser");

action.createParam(PARAM_PASSWORD)
.setDescription("New password")
.setRequired(true)
.setExampleValue("mypassword");

action.createParam(PARAM_PASSWORD_CONFIRMATION)
.setDescription("Must be the same value as \"password\"")
.setRequired(true)
.setExampleValue("mypassword");
}

@Override
public void handle(Request request, Response response) throws Exception {
UserSession.get().checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);

String login = request.mandatoryParam(PARAM_LOGIN);
UpdateUser updateUser = UpdateUser.create(login)
.setPassword(request.mandatoryParam(PARAM_PASSWORD))
.setPasswordConfirmation(request.mandatoryParam(PARAM_PASSWORD_CONFIRMATION));

userUpdater.update(updateUser);
response.noContent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.user.ws;

import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
import org.sonar.core.user.GroupDto;
import org.sonar.core.user.UserDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.user.NewUserNotifier;
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.db.UserGroupDao;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.ws.WsTester;

import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

public class ChangePasswordActionTest {

static final Settings settings = new Settings().setProperty("sonar.defaultGroup", "sonar-users");

@ClassRule
public static final DbTester dbTester = new DbTester();

@ClassRule
public static final EsTester esTester = new EsTester().addDefinitions(new UserIndexDefinition(settings));

WebService.Controller controller;

WsTester tester;

UserIndex index;

DbClient dbClient;

UserIndexer userIndexer;

DbSession session;

@Before
public void setUp() throws Exception {
dbTester.truncateTables();
esTester.truncateIndices();

System2 system2 = new System2();
UserDao userDao = new UserDao(dbTester.myBatis(), system2);
UserGroupDao userGroupDao = new UserGroupDao();
GroupDao groupDao = new GroupDao();
dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), userDao, userGroupDao, groupDao);
session = dbClient.openSession(false);
groupDao.insert(session, new GroupDto().setName("sonar-users"));
session.commit();

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))));
controller = tester.controller("api/users");

MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
}

@After
public void tearDown() throws Exception {
session.close();
}

@Test(expected = ForbiddenException.class)
public void fail_on_missing_permission() throws Exception {
createUser();

MockUserSession.set().setLogin("polop");
tester.newPostRequest("api/users", "change_password")
.setParam("login", "john")
.execute();
}

@Test(expected = NotFoundException.class)
public void fail_on_unknown_user() throws Exception {
tester.newPostRequest("api/users", "change_password")
.setParam("login", "polop")
.setParam("password", "polop")
.setParam("password_confirmation", "polop")
.execute();
}

@Test
public void update_password() throws Exception {
createUser();
session.clearCache();
String originalPassword = dbClient.userDao().selectByLogin(session, "john").getCryptedPassword();

tester.newPostRequest("api/users", "change_password")
.setParam("login", "john")
.setParam("password", "Valar Morghulis")
.setParam("password_confirmation", "Valar Morghulis")
.execute()
.assertNoContent();

session.clearCache();
String newPassword = dbClient.userDao().selectByLogin(session, "john").getCryptedPassword();
assertThat(newPassword).isNotEqualTo(originalPassword);
}

private void createUser() {
dbClient.userDao().insert(session, new UserDto()
.setEmail("john@email.com")
.setLogin("john")
.setName("John")
.setScmAccounts(newArrayList("jn"))
.setActive(true));
session.commit();
userIndexer.index();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ public void setUp() throws Exception {
new CreateAction(mock(UserIndex.class), mock(UserUpdater.class), mock(I18n.class)),
new UpdateAction(mock(UserIndex.class), mock(UserUpdater.class)),
new CurrentUserAction(),
new DeactivateAction(mock(UserIndex.class), mock(UserUpdater.class))));
new DeactivateAction(mock(UserIndex.class), mock(UserUpdater.class)),
new ChangePasswordAction(mock(UserUpdater.class)),
new CurrentUserAction()));
controller = tester.controller("api/users");
}

Expand All @@ -51,7 +53,7 @@ public void define_controller() throws Exception {
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("3.6");
assertThat(controller.actions()).hasSize(5);
assertThat(controller.actions()).hasSize(7);
}

@Test
Expand Down Expand Up @@ -80,6 +82,14 @@ public void define_update_action() throws Exception {
assertThat(action.params()).hasSize(4);
}

@Test
public void define_change_password_action() throws Exception {
WebService.Action action = controller.action("change_password");
assertThat(action).isNotNull();
assertThat(action.isPost()).isTrue();
assertThat(action.params()).hasSize(3);
}

@Test
public void define_deactivate_action() throws Exception {
WebService.Action action = controller.action("deactivate");
Expand Down

0 comments on commit 7a26b1d

Please sign in to comment.