*/
package org.sonar.server.platform;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
-
-import javax.annotation.Nullable;
-
+import com.google.common.collect.Lists;
import org.sonar.api.config.EmailSettings;
import org.sonar.api.issue.action.Actions;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.server.ws.ListingWs;
import org.sonar.server.ws.WebServiceEngine;
-import com.google.common.collect.Lists;
+import javax.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
class ServerComponents {
pico.addSingleton(UsersWs.class);
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.CurrentUserAction.class);
pico.addSingleton(org.sonar.server.user.ws.SearchAction.class);
pico.addSingleton(org.sonar.server.issue.ws.AuthorsAction.class);
--- /dev/null
+/*
+ * 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.api.server.ws.WebService.NewAction;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.user.UserUpdater;
+import org.sonar.server.user.index.UserDoc;
+import org.sonar.server.user.index.UserIndex;
+
+public class DeactivateAction implements BaseUsersWsAction {
+
+ private static final String PARAM_LOGIN = "login";
+
+ private final UserIndex index;
+ private final UserUpdater userUpdater;
+
+ public DeactivateAction(UserIndex index, UserUpdater userUpdater) {
+ this.index = index;
+ this.userUpdater = userUpdater;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ NewAction action = controller.createAction("deactivate")
+ .setDescription("Deactivate a user. Requires Administer System permission")
+ .setSince("3.7")
+ .setPost(true)
+ .setHandler(this);
+
+ action.createParam("login")
+ .setDescription("User login")
+ .setRequired(true)
+ .setExampleValue("myuser");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ UserSession.get().checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+
+ String login = request.mandatoryParam(PARAM_LOGIN);
+ userUpdater.deactivateUserByLogin(login);
+
+ writeResponse(response, login);
+ }
+
+ private void writeResponse(Response response, String login) {
+ UserDoc user = index.getByLogin(login);
+ JsonWriter json = response.newJsonWriter().beginObject();
+ writeUser(json, user);
+ json.endObject().close();
+ }
+
+ private void writeUser(JsonWriter json, UserDoc user) {
+ json.name("user").beginObject()
+ .prop("login", user.login())
+ .prop("name", user.name())
+ .prop("email", user.email())
+ .prop("active", user.active())
+ .name("scmAccounts").beginArray().values(user.scmAccounts()).endArray()
+ .endObject();
+ }
+}
.setDescription("Users management");
defineSearchAction(controller);
- defineDeactivateAction(controller);
for (BaseUsersWsAction action : actions) {
action.define(controller);
}
RailsHandler.addFormatParam(action);
}
-
- private void defineDeactivateAction(NewController controller) {
- NewAction action = controller.createAction("deactivate")
- .setDescription("Deactivate a user. Requires Administer System permission")
- .setSince("3.7")
- .setPost(true)
- .setHandler(RailsHandler.INSTANCE);
-
- action.createParam("login")
- .setDescription("User login")
- .setRequired(true)
- .setExampleValue("myuser");
-
- RailsHandler.addFormatParam(action);
- }
-
}
--- /dev/null
+/*
+ * 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.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.config.Settings;
+import org.sonar.api.i18n.I18n;
+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.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.UserDao;
+import org.sonar.server.user.index.UserDoc;
+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 org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DeactivateActionTest {
+
+ static final Settings settings = new Settings();
+
+ @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;
+
+ @Mock
+ I18n i18n;
+
+ @Before
+ public void setUp() throws Exception {
+ dbTester.truncateTables();
+ esTester.truncateIndices();
+
+ System2 system2 = new System2();
+ UserDao userDao = new UserDao(dbTester.myBatis(), system2);
+ dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), userDao);
+ session = dbClient.openSession(false);
+ session.commit();
+
+ 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))));
+ controller = tester.controller("api/users");
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ session.close();
+ }
+
+ @Test
+ public void deactivate_user() throws Exception {
+ createUser();
+
+ MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ tester.newPostRequest("api/users", "deactivate")
+ .setParam("login", "john")
+ .execute()
+ .assertJson(getClass(), "deactivate_user.json");
+
+ UserDoc user = index.getByLogin("john");
+ assertThat(user.active()).isFalse();
+ }
+
+ @Test(expected = ForbiddenException.class)
+ public void fail_on_missing_permission() throws Exception {
+ createUser();
+
+ MockUserSession.set().setLogin("not_admin");
+ tester.newPostRequest("api/users", "deactivate")
+ .setParam("login", "john").execute();
+ }
+
+ @Test(expected = NotFoundException.class)
+ public void fail_on_unknown_user() throws Exception {
+ MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ tester.newPostRequest("api/users", "deactivate")
+ .setParam("login", "john").execute();
+ }
+
+ private void createUser() {
+ dbClient.userDao().insert(session, new UserDto()
+ .setActive(true)
+ .setEmail("john@email.com")
+ .setLogin("john")
+ .setName("John")
+ .setScmAccounts("jn"));
+ session.commit();
+ userIndexer.index();
+ }
+
+}
WsTester tester = new WsTester(new UsersWs(
new CreateAction(mock(UserIndex.class), mock(UserUpdater.class), mock(I18n.class)),
new UpdateAction(mock(UserIndex.class), mock(UserUpdater.class)),
- new CurrentUserAction()));
+ new CurrentUserAction(),
+ new DeactivateAction(mock(UserIndex.class), mock(UserUpdater.class))));
controller = tester.controller("api/users");
}
WebService.Action action = controller.action("deactivate");
assertThat(action).isNotNull();
assertThat(action.isPost()).isTrue();
- assertThat(action.handler()).isInstanceOf(RailsHandler.class);
- assertThat(action.params()).hasSize(2);
+ assertThat(action.params()).hasSize(1);
}
@Test
--- /dev/null
+{
+ "user": {
+ "login": "john",
+ "name": "John",
+ "email": "john@email.com",
+ "scmAccounts": ["jn"],
+ "active": false
+ }
+}