]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6468 New WS to change a user's password
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Mon, 4 May 2015 13:41:31 +0000 (15:41 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 7 May 2015 12:41:36 +0000 (14:41 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsTest.java

index 14edd65aa0a70c96960fe4b0e67d8b1f7bce1eb7..0fd77b02562fe64d781d6ed8912b713fd03566d8 100644 (file)
@@ -759,6 +759,7 @@ class ServerComponents {
     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);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java
new file mode 100644 (file)
index 0000000..14cbf2e
--- /dev/null
@@ -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();
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/ChangePasswordActionTest.java
new file mode 100644 (file)
index 0000000..037783c
--- /dev/null
@@ -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();
+  }
+}
index 3ab08ea313e241409d87ae7581497d5b5d77d85b..73d65418c7e69f511963318af527b1b4d1ad8cc3 100644 (file)
@@ -42,7 +42,9 @@ public class UsersWsTest {
       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");
   }
 
@@ -51,7 +53,7 @@ public class UsersWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
     assertThat(controller.since()).isEqualTo("3.6");
-    assertThat(controller.actions()).hasSize(5);
+    assertThat(controller.actions()).hasSize(7);
   }
 
   @Test
@@ -80,6 +82,14 @@ public class UsersWsTest {
     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");