]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6494 WS permissions/apply_template apply a permission template to a project
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 1 Sep 2015 06:22:42 +0000 (08:22 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 2 Sep 2015 14:36:19 +0000 (16:36 +0200)
server/sonar-server/src/main/java/org/sonar/server/permission/ws/ApplyTemplateAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionDependenciesFinder.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/WsProjectRef.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/ApplyTemplateActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java

diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/ApplyTemplateAction.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/ApplyTemplateAction.java
new file mode 100644 (file)
index 0000000..d449c7d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.permission.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.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.PermissionTemplateDto;
+import org.sonar.server.permission.ApplyPermissionTemplateQuery;
+import org.sonar.server.permission.PermissionService;
+
+import static java.util.Collections.singletonList;
+import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_ID_EXPLICIT;
+import static org.sonar.server.permission.ws.Parameters.createProjectParameter;
+import static org.sonar.server.permission.ws.Parameters.createExplicitTemplateId;
+
+public class ApplyTemplateAction implements PermissionsWsAction {
+  private final DbClient dbClient;
+  private final PermissionService permissionService;
+  private final PermissionDependenciesFinder finder;
+
+  public ApplyTemplateAction(DbClient dbClient, PermissionService permissionService, PermissionDependenciesFinder finder) {
+    this.dbClient = dbClient;
+    this.permissionService = permissionService;
+    this.finder = finder;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    WebService.NewAction action = context.createAction("apply_template")
+      .setDescription("Apply a permission template to one or several projects.<br />" +
+        "The project id or project key must be provided.<br />" +
+        "It requires administration permissions to access.")
+      .setPost(true)
+      .setSince("5.2")
+      .setHandler(this);
+
+    createExplicitTemplateId(action);
+    createProjectParameter(action);
+  }
+
+  @Override
+  public void handle(Request wsRequest, Response wsResponse) throws Exception {
+    String templateUuid = wsRequest.mandatoryParam(PARAM_TEMPLATE_ID_EXPLICIT);
+
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      PermissionTemplateDto template = finder.getTemplate(dbSession, templateUuid);
+      ComponentDto project = finder.getProject(dbSession, wsRequest);
+
+      ApplyPermissionTemplateQuery query = ApplyPermissionTemplateQuery.create(
+        template.getUuid(),
+        singletonList(project.key()));
+      permissionService.applyPermissionTemplate(query);
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+
+    wsResponse.noContent();
+  }
+}
index 9825d1f34ad0038846b5b91cfdbd0fb89d6ca09a..0fee0e3125809ce8558579a694f6ee2bd5d3660b 100644 (file)
@@ -35,7 +35,6 @@ import static java.lang.String.format;
 import static org.sonar.api.security.DefaultGroups.ANYONE;
 import static org.sonar.api.security.DefaultGroups.isAnyone;
 import static org.sonar.server.ws.WsUtils.checkFound;
-import static org.sonar.server.ws.WsUtils.checkRequest;
 
 public class PermissionDependenciesFinder {
   private final DbClient dbClient;
@@ -59,10 +58,9 @@ public class PermissionDependenciesFinder {
   }
 
   public ComponentDto getProject(DbSession dbSession, Request wsRequest) {
-    Optional<WsProjectRef> projectRef = WsProjectRef.optionalFromRequest(wsRequest);
-    checkRequest(projectRef.isPresent(), "The project id or the project key must be provided.");
+    WsProjectRef projectRef = WsProjectRef.fromRequest(wsRequest);
 
-    return componentFinder.getProjectByUuidOrKey(dbSession, projectRef.get().uuid(), projectRef.get().key());
+    return componentFinder.getProjectByUuidOrKey(dbSession, projectRef.uuid(), projectRef.key());
   }
 
   String getGroupName(DbSession dbSession, PermissionRequest request) {
index 6769088cde549b963272412d71684da8d08bebe7..2c139b1545f6916a97cee89015499f2a96642fc3 100644 (file)
@@ -43,6 +43,7 @@ public class PermissionsWsModule extends Module {
       CreateTemplateAction.class,
       UpdateTemplateAction.class,
       DeleteTemplateAction.class,
+      ApplyTemplateAction.class,
       // utility classes
       PermissionChangeBuilder.class,
       SearchProjectPermissionsDataLoader.class,
index f83c6d210cac5098fa0df00ecc987e4d6d446aad..c51c87b5c8b6a4330eaea2129862e8cf3a1fa569 100644 (file)
@@ -45,7 +45,7 @@ class WsProjectRef {
   }
 
   static Optional<WsProjectRef> optionalFromRequest(Request wsRequest) {
-    if (hasNoProjectParam(wsRequest)) {
+    if (!hasProjectParam(wsRequest)) {
       return Optional.absent();
     }
 
@@ -53,6 +53,8 @@ class WsProjectRef {
   }
 
   static WsProjectRef fromRequest(Request wsRequest) {
+    checkRequest(hasProjectParam(wsRequest), "Project id or project key must be provided, not both.");
+
     return new WsProjectRef(wsRequest);
   }
 
@@ -66,7 +68,7 @@ class WsProjectRef {
     return this.key;
   }
 
-  private static boolean hasNoProjectParam(Request wsRequest) {
-    return !wsRequest.hasParam(PARAM_PROJECT_ID) && !wsRequest.hasParam(PARAM_PROJECT_KEY);
+  private static boolean hasProjectParam(Request wsRequest) {
+    return wsRequest.hasParam(PARAM_PROJECT_ID) || wsRequest.hasParam(PARAM_PROJECT_KEY);
   }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/ApplyTemplateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/ApplyTemplateActionTest.java
new file mode 100644 (file)
index 0000000..33770ed
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * 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.permission.ws;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.GroupWithPermissionDto;
+import org.sonar.db.permission.PermissionQuery;
+import org.sonar.db.permission.PermissionRepository;
+import org.sonar.db.permission.PermissionTemplateDto;
+import org.sonar.db.permission.UserWithPermissionDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.GroupRoleDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserRoleDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.issue.index.IssueAuthorizationIndexer;
+import org.sonar.server.permission.PermissionFinder;
+import org.sonar.server.permission.PermissionService;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.permission.PermissionTemplateTesting.newPermissionTemplateDto;
+import static org.sonar.db.user.GroupMembershipQuery.IN;
+import static org.sonar.db.user.GroupTesting.newGroupDto;
+import static org.sonar.db.user.UserTesting.newUserDto;
+import static org.sonar.server.permission.ws.Parameters.PARAM_PROJECT_ID;
+import static org.sonar.server.permission.ws.Parameters.PARAM_PROJECT_KEY;
+import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_ID_EXPLICIT;
+
+@Category(DbTests.class)
+public class ApplyTemplateActionTest {
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  WsActionTester ws;
+  DbClient dbClient;
+  DbSession dbSession;
+
+  UserDto user1;
+  UserDto user2;
+  GroupDto group1;
+  GroupDto group2;
+  ComponentDto project;
+  PermissionTemplateDto template1;
+  PermissionTemplateDto template2;
+  IssueAuthorizationIndexer issueAuthorizationIndexer = mock(IssueAuthorizationIndexer.class);
+
+  @Before
+  public void setUp() {
+    userSession.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+    dbClient = db.getDbClient();
+    dbSession = db.getSession();
+
+    PermissionRepository repository = new PermissionRepository(dbClient, new Settings());
+    PermissionFinder permissionFinder = new PermissionFinder(dbClient);
+    ComponentFinder componentFinder = new ComponentFinder(dbClient);
+    PermissionService permissionService = new PermissionService(dbClient, repository, permissionFinder, issueAuthorizationIndexer, userSession, componentFinder);
+    PermissionDependenciesFinder permissionDependenciesFinder = new PermissionDependenciesFinder(dbClient, componentFinder);
+
+    ApplyTemplateAction underTest = new ApplyTemplateAction(dbClient, permissionService, permissionDependenciesFinder);
+    ws = new WsActionTester(underTest);
+
+    user1 = insertUser(newUserDto().setLogin("user-login-1"));
+    user2 = insertUser(newUserDto().setLogin("user-login-2"));
+    group1 = insertGroup(newGroupDto().setName("group-name-1"));
+    group2 = insertGroup(newGroupDto().setName("group-name-2"));
+
+    // template 1
+    template1 = insertTemplate(newPermissionTemplateDto().setUuid("permission-template-uuid-1"));
+    addUserToTemplate(user1, template1, UserRole.CODEVIEWER);
+    addUserToTemplate(user2, template1, UserRole.ISSUE_ADMIN);
+    addGroupToTemplate(group1, template1, UserRole.ADMIN);
+    addGroupToTemplate(group2, template1, UserRole.USER);
+    // template 2
+    template2 = insertTemplate(newPermissionTemplateDto().setUuid("permission-template-uuid-2"));
+    addUserToTemplate(user1, template2, UserRole.USER);
+    addUserToTemplate(user2, template2, UserRole.USER);
+    addGroupToTemplate(group1, template2, UserRole.USER);
+    addGroupToTemplate(group2, template2, UserRole.USER);
+
+    project = insertProject(newProjectDto("project-uuid-1"));
+    addUserPermissionToProject(user1, project, UserRole.ADMIN);
+    addUserPermissionToProject(user2, project, UserRole.ADMIN);
+    addGroupPermissionToProject(group1, project, UserRole.ADMIN);
+    addGroupPermissionToProject(group2, project, UserRole.ADMIN);
+
+    commit();
+  }
+
+  @Test
+  public void apply_template_with_project_uuid() {
+    assertThat(selectProjectPermissionGroups(project, UserRole.ADMIN)).hasSize(2);
+    assertThat(selectProjectPermissionUsers(project, UserRole.ADMIN)).hasSize(2);
+
+    newRequest(template1.getUuid(), project.uuid(), null);
+
+    assertTemplate1AppliedToProject();
+    verify(issueAuthorizationIndexer).index();
+  }
+
+  @Test
+  public void apply_template_with_project_key() {
+    newRequest(template1.getUuid(), null, project.key());
+
+    assertTemplate1AppliedToProject();
+  }
+
+  @Test
+  public void fail_when_unknown_template() {
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Permission template with id 'unknown-template-uuid' is not found");
+
+    newRequest("unknown-template-uuid", project.uuid(), null);
+  }
+
+  @Test
+  public void fail_when_unknown_project_uuid() {
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Project id 'unknown-project-uuid' not found");
+
+    newRequest(template1.getUuid(), "unknown-project-uuid", null);
+  }
+
+  @Test
+  public void fail_when_unknown_project_key() {
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("Project key 'unknown-project-key' not found");
+
+    newRequest(template1.getUuid(), null, "unknown-project-key");
+  }
+
+  @Test
+  public void fail_when_template_is_not_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    newRequest(null, project.uuid(), null);
+  }
+
+  @Test
+  public void fail_when_project_uuid_and_key_not_provided() {
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Project id or project key must be provided, not both.");
+
+    newRequest(template1.getUuid(), null, null);
+  }
+
+  @Test
+  public void fail_when_anonymous() {
+    expectedException.expect(UnauthorizedException.class);
+    userSession.anonymous();
+
+    newRequest(template1.getUuid(), project.uuid(), null);
+  }
+
+  @Test
+  public void fail_when_insufficient_privileges() {
+    expectedException.expect(ForbiddenException.class);
+    userSession.login().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+
+    newRequest(template1.getUuid(), project.uuid(), null);
+  }
+
+  private void assertTemplate1AppliedToProject() {
+    assertThat(selectProjectPermissionGroups(project, UserRole.ADMIN)).extracting("name").containsExactly(group1.getName());
+    assertThat(selectProjectPermissionGroups(project, UserRole.USER)).extracting("name").containsExactly(group2.getName());
+    assertThat(selectProjectPermissionUsers(project, UserRole.ADMIN)).isEmpty();
+    assertThat(selectProjectPermissionUsers(project, UserRole.CODEVIEWER)).extracting("login").containsExactly(user1.getLogin());
+    assertThat(selectProjectPermissionUsers(project, UserRole.ISSUE_ADMIN)).extracting("login").containsExactly(user2.getLogin());
+  }
+
+  private TestResponse newRequest(@Nullable String templateUuid, @Nullable String projectUuid, @Nullable String projectKey) {
+    TestRequest request = ws.newRequest();
+    if (templateUuid != null) {
+      request.setParam(PARAM_TEMPLATE_ID_EXPLICIT, templateUuid);
+    }
+    if (projectUuid != null) {
+      request.setParam(PARAM_PROJECT_ID, projectUuid);
+    }
+    if (projectKey != null) {
+      request.setParam(PARAM_PROJECT_KEY, projectKey);
+    }
+
+    TestResponse result = request.execute();
+    commit();
+
+    return result;
+  }
+
+  private ComponentDto insertProject(ComponentDto project) {
+    dbClient.componentDao().insert(dbSession, project);
+    return dbClient.componentDao().selectOrFailByUuid(dbSession, project.uuid());
+  }
+
+  private PermissionTemplateDto insertTemplate(PermissionTemplateDto template) {
+    return dbClient.permissionTemplateDao().insert(dbSession, template);
+  }
+
+  private UserDto insertUser(UserDto userDto) {
+    return dbClient.userDao().insert(dbSession, userDto.setActive(true));
+  }
+
+  private GroupDto insertGroup(GroupDto group) {
+    return dbClient.groupDao().insert(dbSession, group);
+  }
+
+  private void addUserToTemplate(UserDto user, PermissionTemplateDto permissionTemplate, String permission) {
+    dbClient.permissionTemplateDao().insertUserPermission(dbSession, permissionTemplate.getId(), user.getId(), permission);
+  }
+
+  private void addGroupToTemplate(GroupDto group, PermissionTemplateDto permissionTemplate, String permission) {
+    dbClient.permissionTemplateDao().insertGroupPermission(dbSession, permissionTemplate.getId(), group.getId(), permission);
+  }
+
+  private void addUserPermissionToProject(UserDto user, ComponentDto project, String permission) {
+    dbClient.roleDao().insertUserRole(dbSession, new UserRoleDto()
+      .setRole(permission)
+      .setUserId(user.getId())
+      .setResourceId(project.getId()));
+  }
+
+  private void addGroupPermissionToProject(GroupDto group, ComponentDto project, String permission) {
+    dbClient.roleDao().insertGroupRole(dbSession, new GroupRoleDto()
+      .setRole(permission)
+      .setResourceId(project.getId())
+      .setGroupId(group.getId()));
+  }
+
+  private List<GroupWithPermissionDto> selectProjectPermissionGroups(ComponentDto project, String permission) {
+    return FluentIterable.from(dbClient.permissionDao().selectGroups(dbSession, query(permission), project.getId()))
+      .filter(new PermissionNotNull())
+      .toList();
+  }
+
+  private List<UserWithPermissionDto> selectProjectPermissionUsers(ComponentDto project, String permission) {
+    return dbClient.permissionDao().selectUsers(dbSession, query(permission), project.getId(), 0, Integer.MAX_VALUE);
+  }
+
+  private void commit() {
+    dbSession.commit();
+  }
+
+  private static PermissionQuery query(String permission) {
+    return PermissionQuery.builder().membership(IN).permission(permission).build();
+  }
+
+  private static class PermissionNotNull implements Predicate<GroupWithPermissionDto> {
+    @Override
+    public boolean apply(@Nullable GroupWithPermissionDto input) {
+      return input.getPermission() != null;
+    }
+  }
+}
index c8c59d230901cc44f346b28de3fff7f5f774ea2f..37575dd01c182a86142e45601bc84f5e91a30ebe 100644 (file)
@@ -30,6 +30,6 @@ public class PermissionsWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new PermissionsWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(22);
+    assertThat(container.size()).isEqualTo(23);
   }
 }