diff options
author | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2015-08-24 08:23:42 +0200 |
---|---|---|
committer | Teryk Bellahsene <teryk.bellahsene@sonarsource.com> | 2015-08-24 10:38:51 +0200 |
commit | ce39ae27e9fdcab9b379bf5d9eb552e78e0d9416 (patch) | |
tree | 3bb3773cf0b728ef4724fee9f26199f5879c3ff3 | |
parent | 0e271c15e6aa0e77fb1edc1ba42d0589948d141d (diff) | |
download | sonarqube-ce39ae27e9fdcab9b379bf5d9eb552e78e0d9416.tar.gz sonarqube-ce39ae27e9fdcab9b379bf5d9eb552e78e0d9416.zip |
SONAR-6502 WS permissions/add_group_to_template add a group to a permission template
19 files changed, 479 insertions, 84 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionValueValidator.java b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionRequestValidator.java index e8a4a2d92f0..9ce5d35934b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionValueValidator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionRequestValidator.java @@ -20,26 +20,31 @@ package org.sonar.server.permission; -import org.sonar.core.permission.ProjectPermissions; +import javax.annotation.Nullable; import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.exceptions.BadRequestException; +import org.sonar.core.permission.ProjectPermissions; + +import static java.lang.String.format; +import static org.sonar.api.security.DefaultGroups.isAnyone; +import static org.sonar.server.ws.WsUtils.checkRequest; -public class PermissionValueValidator { - private PermissionValueValidator() { +public class PermissionRequestValidator { + private PermissionRequestValidator() { // static methods only } public static void validateProjectPermission(String permission) { - if (!ProjectPermissions.ALL.contains(permission)) { - throw new BadRequestException(String.format("The 'permission' parameter for project permissions must be one of %s. '%s' was passed.", ProjectPermissions.ALL_ON_ONE_LINE, - permission)); - } + checkRequest(ProjectPermissions.ALL.contains(permission), + format("The 'permission' parameter for project permissions must be one of %s. '%s' was passed.", ProjectPermissions.ALL_ON_ONE_LINE, permission)); } public static void validateGlobalPermission(String permission) { - if (!GlobalPermissions.ALL.contains(permission)) { - throw new BadRequestException(String.format("The 'permission' parameter for global permissions must be one of %s. '%s' was passed.", GlobalPermissions.ALL_ON_ONE_LINE, - permission)); - } + checkRequest(GlobalPermissions.ALL.contains(permission), + format("The 'permission' parameter for global permissions must be one of %s. '%s' was passed.", GlobalPermissions.ALL_ON_ONE_LINE, permission)); + } + + public static void validateNotAnyoneAndAdminPermission(String permission, @Nullable String groupName) { + checkRequest(!GlobalPermissions.SYSTEM_ADMIN.equals(permission) || !isAnyone(groupName), + String.format("It is not possible to add the '%s' permission to the '%s' group.", permission, groupName)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateUpdater.java index 4f3970f642b..28063e8bdaf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionTemplateUpdater.java @@ -30,7 +30,7 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.user.UserSession; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdminUser; -import static org.sonar.server.permission.PermissionValueValidator.validateProjectPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateProjectPermission; /** * @deprecated since 5.2 can be removed when Ruby doesn't rely on PermissionTemplateService diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionUpdater.java index d1f32625034..27be8f1c8ae 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/PermissionUpdater.java @@ -23,7 +23,6 @@ package org.sonar.server.permission; import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import org.sonar.api.security.DefaultGroups; import org.sonar.core.permission.GlobalPermissions; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -36,7 +35,9 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.issue.index.IssueAuthorizationIndexer; import org.sonar.server.user.UserSession; +import static org.sonar.api.security.DefaultGroups.isAnyone; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkProjectAdminUserByComponentKey; +import static org.sonar.server.permission.PermissionRequestValidator.validateNotAnyoneAndAdminPermission; public class PermissionUpdater { @@ -114,7 +115,7 @@ public class PermissionUpdater { Long targetedGroup = getTargetedGroup(session, permissionChange.groupName()); String permission = permissionChange.permission(); if (Operation.ADD == operation) { - checkNotAnyoneAndAdmin(permission, permissionChange.groupName()); + validateNotAnyoneAndAdminPermission(permission, permissionChange.groupName()); permissionRepository.insertGroupPermission(componentId, targetedGroup, permission, session); } else { checkAdminUsersExistOutsideTheRemovedGroup(session, permissionChange, targetedGroup); @@ -123,13 +124,6 @@ public class PermissionUpdater { return true; } - private static void checkNotAnyoneAndAdmin(String permission, String group) { - if (GlobalPermissions.SYSTEM_ADMIN.equals(permission) - && DefaultGroups.isAnyone(group)) { - throw new BadRequestException(String.format("It is not possible to add the '%s' permission to the '%s' group.", permission, group)); - } - } - private boolean applyChangeOnUser(DbSession session, Operation operation, PermissionChange permissionChange) { Long componentId = getComponentId(session, permissionChange.componentKey()); checkProjectAdminUserByComponentKey(userSession, permissionChange.componentKey()); @@ -173,7 +167,7 @@ public class PermissionUpdater { @Nullable private Long getTargetedGroup(DbSession session, String group) { - if (DefaultGroups.isAnyone(group)) { + if (isAnyone(group)) { return null; } else { GroupDto groupDto = dbClient.groupDao().selectByName(session, group); diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddGroupToTemplateAction.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddGroupToTemplateAction.java new file mode 100644 index 00000000000..6e35f17edd0 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddGroupToTemplateAction.java @@ -0,0 +1,109 @@ +/* + * 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 javax.annotation.Nullable; +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.permission.PermissionQuery; +import org.sonar.db.permission.PermissionTemplateDto; +import org.sonar.db.user.GroupDto; +import org.sonar.server.user.UserSession; + +import static org.sonar.api.security.DefaultGroups.ANYONE; +import static org.sonar.db.user.GroupMembershipQuery.IN; +import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdminUser; +import static org.sonar.server.permission.PermissionRequestValidator.validateNotAnyoneAndAdminPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateProjectPermission; +import static org.sonar.server.permission.ws.Parameters.PARAM_GROUP_ID; +import static org.sonar.server.permission.ws.Parameters.PARAM_GROUP_NAME; +import static org.sonar.server.permission.ws.Parameters.PARAM_PERMISSION; +import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_KEY; +import static org.sonar.server.permission.ws.Parameters.createGroupIdParameter; +import static org.sonar.server.permission.ws.Parameters.createGroupNameParameter; +import static org.sonar.server.permission.ws.Parameters.createPermissionParameter; +import static org.sonar.server.permission.ws.Parameters.createTemplateKeyParameter; + +public class AddGroupToTemplateAction implements PermissionsWsAction { + private final DbClient dbClient; + private final PermissionDependenciesFinder dependenciesFinder; + private final UserSession userSession; + + public AddGroupToTemplateAction(DbClient dbClient, PermissionDependenciesFinder dependenciesFinder, UserSession userSession) { + this.dbClient = dbClient; + this.dependenciesFinder = dependenciesFinder; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context + .createAction("add_group_to_template") + .setPost(true) + .setSince("5.2") + .setDescription("Add a group to a permission template.<br /> " + + "The group id or group name must be provided. <br />" + + "It requires administration permissions to access.") + .setHandler(this); + + createTemplateKeyParameter(action); + createPermissionParameter(action); + createGroupIdParameter(action); + createGroupNameParameter(action); + } + + @Override + public void handle(Request wsRequest, Response wsResponse) throws Exception { + checkGlobalAdminUser(userSession); + + String templateKey = wsRequest.mandatoryParam(PARAM_TEMPLATE_KEY); + String permission = wsRequest.mandatoryParam(PARAM_PERMISSION); + Long groupIdParam = wsRequest.paramAsLong(PARAM_GROUP_ID); + String groupName = wsRequest.param(PARAM_GROUP_NAME); + + DbSession dbSession = dbClient.openSession(false); + try { + validateProjectPermission(permission); + validateNotAnyoneAndAdminPermission(permission, groupName); + + PermissionTemplateDto template = dependenciesFinder.getTemplate(templateKey); + GroupDto group = dependenciesFinder.getGroup(dbSession, groupIdParam, groupName); + + if (!groupAlreadyAdded(dbSession, template.getId(), group, permission)) { + Long groupId = group == null ? null : group.getId(); + dbClient.permissionTemplateDao().insertGroupPermission(dbSession, template.getId(), groupId, permission); + } + } finally { + dbClient.closeSession(dbSession); + } + + wsResponse.noContent(); + } + + private boolean groupAlreadyAdded(DbSession dbSession, long templateId, @Nullable GroupDto group, String permission) { + String groupName = group == null ? ANYONE : group.getName(); + PermissionQuery permissionQuery = PermissionQuery.builder().membership(IN).permission(permission).build(); + return dbClient.permissionTemplateDao().hasGroup(dbSession, permissionQuery, templateId, groupName); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddUserToTemplateAction.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddUserToTemplateAction.java index 78b5e7ec988..01ae1255d4c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddUserToTemplateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/AddUserToTemplateAction.java @@ -36,7 +36,7 @@ import org.sonar.server.user.UserSession; import static com.google.common.collect.FluentIterable.from; import static org.sonar.db.user.GroupMembershipQuery.IN; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdminUser; -import static org.sonar.server.permission.PermissionValueValidator.validateProjectPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateProjectPermission; import static org.sonar.server.permission.ws.Parameters.PARAM_PERMISSION; import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_KEY; import static org.sonar.server.permission.ws.Parameters.PARAM_USER_LOGIN; diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionDependenciesFinder.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionDependenciesFinder.java index 80db297ef98..32d73c48a08 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionDependenciesFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionDependenciesFinder.java @@ -22,6 +22,7 @@ package org.sonar.server.permission.ws; import com.google.common.base.Optional; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; @@ -29,11 +30,12 @@ import org.sonar.db.permission.PermissionTemplateDto; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.component.ComponentFinder; -import org.sonar.server.exceptions.BadRequestException; -import static com.google.common.base.Preconditions.checkNotNull; +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; @@ -56,57 +58,44 @@ public class PermissionDependenciesFinder { } String getGroupName(DbSession dbSession, PermissionRequest request) { - if (isAnyone(request.groupName())) { - return ANYONE; + GroupDto group = getGroup(dbSession, request.groupId(), request.groupName()); + + return group == null ? ANYONE : group.getName(); + } + + /** + * + * @return null if it's the anyone group + */ + @CheckForNull + GroupDto getGroup(DbSession dbSession, @Nullable Long groupId, @Nullable String groupName) { + checkRequest(groupId != null ^ groupName != null, "Group name or group id must be provided, not both."); + if (isAnyone(groupName)) { + return null; } GroupDto group = null; - Long groupId = request.groupId(); if (groupId != null) { - group = dbClient.groupDao().selectById(dbSession, groupId); - if (group == null) { - throw new BadRequestException(String.format("Group with id '%d' is not found", groupId)); - } + group = checkFound(dbClient.groupDao().selectById(dbSession, groupId), + format("Group with id '%d' is not found", groupId)); } - String groupName = request.groupName(); if (groupName != null) { - group = dbClient.groupDao().selectByName(dbSession, groupName); - if (group == null) { - throw new BadRequestException(String.format("Group with name '%s' is not found", groupName)); - } - } - - return checkNotNull(group).getName(); - } - - @CheckForNull - Long getGroupId(DbSession dbSession, String groupName) { - if (isAnyone(groupName)) { - return null; + group = checkFound(dbClient.groupDao().selectByName(dbSession, groupName), + format("Group with name '%s' is not found", groupName)); } - GroupDto group = dbClient.groupDao().selectByName(dbSession, groupName); - if (group == null) { - throw new BadRequestException(String.format("Group with name '%s' is not found", groupName)); - } - return group.getId(); + return group; } UserDto getUser(DbSession dbSession, String userLogin) { - UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin); - if (user == null) { - throw new BadRequestException(String.format("User with login '%s' is not found'", userLogin)); - } - return user; + return checkFound(dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin), + format("User with login '%s' is not found'", userLogin)); } PermissionTemplateDto getTemplate(String templateKey) { - PermissionTemplateDto permissionTemplate = dbClient.permissionTemplateDao().selectTemplateByKey(templateKey); - if (permissionTemplate == null) { - throw new BadRequestException(String.format("Permission template with key '%s' is not found", templateKey)); - } - return permissionTemplate; + return checkFound(dbClient.permissionTemplateDao().selectTemplateByKey(templateKey), + format("Permission template with key '%s' is not found", templateKey)); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionRequest.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionRequest.java index d043ea3a942..a6a87a7106d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionRequest.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionRequest.java @@ -34,8 +34,8 @@ import static org.sonar.server.permission.ws.Parameters.PARAM_PERMISSION; import static org.sonar.server.permission.ws.Parameters.PARAM_PROJECT_KEY; import static org.sonar.server.permission.ws.Parameters.PARAM_PROJECT_UUID; import static org.sonar.server.permission.ws.Parameters.PARAM_USER_LOGIN; -import static org.sonar.server.permission.PermissionValueValidator.validateGlobalPermission; -import static org.sonar.server.permission.PermissionValueValidator.validateProjectPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateGlobalPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateProjectPermission; import static org.sonar.server.ws.WsUtils.checkRequest; class PermissionRequest { diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java index 18bf09ceddf..f7434324cbd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java @@ -39,6 +39,7 @@ public class PermissionsWsModule extends Module { SearchProjectPermissionsDataLoader.class, PermissionDependenciesFinder.class, AddUserToTemplateAction.class, - RemoveUserFromTemplateAction.class); + RemoveUserFromTemplateAction.class, + AddGroupToTemplateAction.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/RemoveUserFromTemplateAction.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/RemoveUserFromTemplateAction.java index cdd5afe0772..7b2857e21ef 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/RemoveUserFromTemplateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/RemoveUserFromTemplateAction.java @@ -30,7 +30,7 @@ import org.sonar.db.user.UserDto; import org.sonar.server.user.UserSession; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdminUser; -import static org.sonar.server.permission.PermissionValueValidator.validateProjectPermission; +import static org.sonar.server.permission.PermissionRequestValidator.validateProjectPermission; import static org.sonar.server.permission.ws.Parameters.PARAM_PERMISSION; import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_KEY; import static org.sonar.server.permission.ws.Parameters.PARAM_USER_LOGIN; diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java b/server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java index 707cbbde6c5..b96a67b7235 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ws/WsUtils.java @@ -28,6 +28,7 @@ import org.sonar.api.server.ws.Response; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.util.ProtobufJsonFormat; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.plugins.MimeTypes; public class WsUtils { @@ -56,9 +57,21 @@ public class WsUtils { /** * @throws BadRequestException */ - public static void checkRequest(boolean expression, String errorMessage) { + public static void checkRequest(boolean expression, String message) { if (!expression) { - throw new BadRequestException(errorMessage); + throw new BadRequestException(message); } } + + /** + * @throws NotFoundException if the value if null + * @return the value + */ + public static <T> T checkFound(T value, String message) { + if (value == null) { + throw new NotFoundException(message); + } + + return value; + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupToTemplateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupToTemplateActionTest.java new file mode 100644 index 00000000000..221fbfcf76e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupToTemplateActionTest.java @@ -0,0 +1,241 @@ +/* + * 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.Function; +import java.util.List; +import javax.annotation.Nonnull; +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.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.permission.GroupWithPermissionDto; +import org.sonar.db.permission.PermissionQuery; +import org.sonar.db.permission.PermissionTemplateDto; +import org.sonar.db.user.GroupDto; +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.tester.UserSessionRule; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; +import org.sonar.test.DbTests; + +import static com.google.common.collect.FluentIterable.from; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.security.DefaultGroups.ANYONE; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.api.web.UserRole.CODEVIEWER; +import static org.sonar.api.web.UserRole.ISSUE_ADMIN; +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.server.permission.ws.Parameters.PARAM_GROUP_ID; +import static org.sonar.server.permission.ws.Parameters.PARAM_PERMISSION; +import static org.sonar.server.permission.ws.Parameters.PARAM_TEMPLATE_KEY; + +@Category(DbTests.class) +public class AddGroupToTemplateActionTest { + + private static final String GROUP_NAME = "group-name"; + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + WsActionTester ws; + DbClient dbClient; + DbSession dbSession; + GroupDto group; + PermissionTemplateDto permissionTemplate; + + @Before + public void setUp() { + dbClient = db.getDbClient(); + dbSession = db.getSession(); + userSession.login().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + + PermissionDependenciesFinder dependenciesFinder = new PermissionDependenciesFinder(dbClient, new ComponentFinder(dbClient)); + ws = new WsActionTester(new AddGroupToTemplateAction(dbClient, dependenciesFinder, userSession)); + + group = insertGroup(newGroupDto().setName(GROUP_NAME)); + permissionTemplate = insertPermissionTemplate(newPermissionTemplateDto()); + commit(); + } + + @Test + public void add_group_to_template() { + newRequest(GROUP_NAME, permissionTemplate.getKee(), CODEVIEWER); + + assertThat(getGroupNamesInTemplateAndPermission(permissionTemplate.getId(), CODEVIEWER)).containsExactly(GROUP_NAME); + } + + @Test + public void add_with_group_id() { + ws.newRequest() + .setParam(PARAM_TEMPLATE_KEY, permissionTemplate.getKee()) + .setParam(PARAM_PERMISSION, CODEVIEWER) + .setParam(PARAM_GROUP_ID, String.valueOf(group.getId())) + .execute(); + + assertThat(getGroupNamesInTemplateAndPermission(permissionTemplate.getId(), CODEVIEWER)).containsExactly(GROUP_NAME); + } + + @Test + public void does_not_add_a_group_twice() { + newRequest(GROUP_NAME, permissionTemplate.getKee(), ISSUE_ADMIN); + newRequest(GROUP_NAME, permissionTemplate.getKee(), ISSUE_ADMIN); + + assertThat(getGroupNamesInTemplateAndPermission(permissionTemplate.getId(), ISSUE_ADMIN)).containsExactly(GROUP_NAME); + } + + @Test + public void add_anyone_group_to_template() { + newRequest(ANYONE, permissionTemplate.getKee(), CODEVIEWER); + + assertThat(getGroupNamesInTemplateAndPermission(permissionTemplate.getId(), CODEVIEWER)).containsExactly(ANYONE); + } + + @Test + public void fail_if_add_anyone_group_to_admin_permission() { + expectedException.expect(BadRequestException.class); + expectedException.expectMessage(String.format("It is not possible to add the '%s' permission to the '%s' group.", UserRole.ADMIN, ANYONE)); + + newRequest(ANYONE, permissionTemplate.getKee(), ADMIN); + } + + @Test + public void fail_if_not_a_project_permission() { + expectedException.expect(BadRequestException.class); + + newRequest(GROUP_NAME, permissionTemplate.getKee(), GlobalPermissions.PREVIEW_EXECUTION); + } + + @Test + public void fail_if_insufficient_privileges() { + expectedException.expect(ForbiddenException.class); + userSession.setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN); + + newRequest(GROUP_NAME, permissionTemplate.getKee(), CODEVIEWER); + } + + @Test + public void fail_if_not_logged_in() { + expectedException.expect(UnauthorizedException.class); + userSession.anonymous(); + + newRequest(GROUP_NAME, permissionTemplate.getKee(), CODEVIEWER); + } + + @Test + public void fail_if_group_params_missing() { + expectedException.expect(BadRequestException.class); + + newRequest(null, permissionTemplate.getKee(), CODEVIEWER); + } + + @Test + public void fail_if_permission_missing() { + expectedException.expect(IllegalArgumentException.class); + + newRequest(GROUP_NAME, permissionTemplate.getKee(), null); + } + + @Test + public void fail_if_template_key_missing() { + expectedException.expect(IllegalArgumentException.class); + + newRequest(GROUP_NAME, null, CODEVIEWER); + } + + @Test + public void fail_if_group_does_not_exist() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Group with name 'unknown-group-name' is not found"); + + newRequest("unknown-group-name", permissionTemplate.getKee(), CODEVIEWER); + } + + @Test + public void fail_if_template_key_does_not_exist() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Permission template with key 'unknown-key' is not found"); + + newRequest(GROUP_NAME, "unknown-key", CODEVIEWER); + } + + private void newRequest(@Nullable String groupName, @Nullable String templateKey, @Nullable String permission) { + TestRequest request = ws.newRequest(); + if (groupName != null) { + request.setParam(Parameters.PARAM_GROUP_NAME, groupName); + } + if (templateKey != null) { + request.setParam(PARAM_TEMPLATE_KEY, templateKey); + } + if (permission != null) { + request.setParam(Parameters.PARAM_PERMISSION, permission); + } + + request.execute(); + } + + private void commit() { + dbSession.commit(); + } + + private GroupDto insertGroup(GroupDto groupDto) { + return dbClient.groupDao().insert(dbSession, groupDto); + } + + private PermissionTemplateDto insertPermissionTemplate(PermissionTemplateDto permissionTemplate) { + return dbClient.permissionTemplateDao().insertPermissionTemplate(permissionTemplate.getName(), permissionTemplate.getDescription(), permissionTemplate.getKeyPattern()); + } + + private List<String> getGroupNamesInTemplateAndPermission(long templateId, String permission) { + PermissionQuery permissionQuery = PermissionQuery.builder().permission(permission).membership(IN).build(); + return from(dbClient.permissionTemplateDao() + .selectGroups(dbSession, permissionQuery, templateId)) + .transform(GroupWithPermissionToGroupName.INSTANCE) + .toList(); + } + + private enum GroupWithPermissionToGroupName implements Function<GroupWithPermissionDto, String> { + INSTANCE; + + @Override + public String apply(@Nonnull GroupWithPermissionDto groupWithPermission) { + return groupWithPermission.getName(); + } + + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserToTemplateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserToTemplateActionTest.java index 957fdcf2691..a4fdf5f09cc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserToTemplateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserToTemplateActionTest.java @@ -41,6 +41,7 @@ import org.sonar.db.user.UserDto; 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.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; @@ -147,7 +148,7 @@ public class AddUserToTemplateActionTest { @Test public void fail_if_user_does_not_exist() { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("User with login 'unknown-login' is not found"); newRequest("unknown-login", permissionTemplate.getKee(), CODEVIEWER); @@ -155,7 +156,7 @@ public class AddUserToTemplateActionTest { @Test public void fail_if_template_key_does_not_exist() { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("Permission template with key 'unknown-key' is not found"); newRequest(USER_LOGIN, "unknown-key", CODEVIEWER); diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java index b609653ed9d..cabe737a907 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java @@ -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(16); + assertThat(container.size()).isEqualTo(17); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java index 016cf0aab0d..4e85fdb1efe 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java @@ -35,6 +35,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.user.GroupDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.ServerException; import org.sonar.server.permission.PermissionChange; import org.sonar.server.permission.PermissionUpdater; @@ -144,7 +145,7 @@ public class RemoveGroupActionTest { @Test public void fail_when_project_does_not_exist() throws Exception { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); newRequest() .setParam(PARAM_GROUP_NAME, "sonar-administrators") @@ -207,7 +208,7 @@ public class RemoveGroupActionTest { @Test public void fail_when_group_id_does_not_exist() throws Exception { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("Group with id '42' is not found"); newRequest() diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserFromTemplateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserFromTemplateActionTest.java index 080a7b1e0f3..93bb60e77af 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserFromTemplateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserFromTemplateActionTest.java @@ -41,6 +41,7 @@ import org.sonar.db.user.UserDto; 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.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; @@ -171,7 +172,7 @@ public class RemoveUserFromTemplateActionTest { @Test public void fail_if_user_does_not_exist() { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("User with login 'unknown-login' is not found"); newRequest("unknown-login", permissionTemplate.getKee(), DEFAULT_PERMISSION); @@ -179,7 +180,7 @@ public class RemoveUserFromTemplateActionTest { @Test public void fail_if_template_key_does_not_exist() { - expectedException.expect(BadRequestException.class); + expectedException.expect(NotFoundException.class); expectedException.expectMessage("Permission template with key 'unknown-key' is not found"); newRequest(USER_LOGIN, "unknown-key", DEFAULT_PERMISSION); diff --git a/sonar-db/src/main/java/org/sonar/core/permission/ProjectPermissions.java b/sonar-db/src/main/java/org/sonar/core/permission/ProjectPermissions.java index e7365f8232b..26e86f41ff3 100644 --- a/sonar-db/src/main/java/org/sonar/core/permission/ProjectPermissions.java +++ b/sonar-db/src/main/java/org/sonar/core/permission/ProjectPermissions.java @@ -31,6 +31,10 @@ import org.sonar.api.web.UserRole; */ public final class ProjectPermissions { + private ProjectPermissions() { + // static constants only + } + /** * All the component permissions values, ordered from {@link UserRole#USER} to {@link UserRole#CODEVIEWER}. */ diff --git a/sonar-db/src/main/java/org/sonar/db/permission/PermissionTemplateDao.java b/sonar-db/src/main/java/org/sonar/db/permission/PermissionTemplateDao.java index b5c58686816..1170be201d1 100644 --- a/sonar-db/src/main/java/org/sonar/db/permission/PermissionTemplateDao.java +++ b/sonar-db/src/main/java/org/sonar/db/permission/PermissionTemplateDao.java @@ -103,11 +103,22 @@ public class PermissionTemplateDao implements Dao { } } - public int countGroups(DbSession session, PermissionQuery query, Long templateId) { + public int countGroups(DbSession session, PermissionQuery query, long templateId) { + return countGroups(session, query, templateId, null); + } + + private int countGroups(DbSession session, PermissionQuery query, long templateId, @Nullable String groupName) { Map<String, Object> parameters = groupsParamaters(query, templateId); + if (groupName != null) { + parameters.put("groupName", groupName.toUpperCase()); + } return mapper(session).countGroups(parameters); } + public boolean hasGroup(DbSession session, PermissionQuery query, long templateId, String groupName) { + return countGroups(session, query, templateId, groupName) > 0; + } + private static Map<String, Object> groupsParamaters(PermissionQuery query, Long templateId) { Map<String, Object> params = newHashMap(); params.put(QUERY_PARAMETER, query); @@ -266,19 +277,23 @@ public class PermissionTemplateDao implements Dao { } public void insertGroupPermission(Long templateId, @Nullable Long groupId, String permission) { + DbSession session = myBatis.openSession(false); + try { + insertGroupPermission(session, templateId, groupId, permission); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void insertGroupPermission(DbSession session, Long templateId, @Nullable Long groupId, String permission) { PermissionTemplateGroupDto permissionTemplateGroup = new PermissionTemplateGroupDto() .setTemplateId(templateId) .setPermission(permission) .setGroupId(groupId) .setCreatedAt(now()) .setUpdatedAt(now()); - SqlSession session = myBatis.openSession(false); - try { - mapper(session).insertGroupPermission(permissionTemplateGroup); - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } + mapper(session).insertGroupPermission(permissionTemplateGroup); + session.commit(); } public void deleteGroupPermission(Long templateId, @Nullable Long groupId, String permission) { diff --git a/sonar-db/src/main/resources/org/sonar/db/permission/PermissionTemplateMapper.xml b/sonar-db/src/main/resources/org/sonar/db/permission/PermissionTemplateMapper.xml index 260baeb1d90..ce3496d303f 100644 --- a/sonar-db/src/main/resources/org/sonar/db/permission/PermissionTemplateMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/permission/PermissionTemplateMapper.xml @@ -119,6 +119,14 @@ <if test="query.search() != null"> AND (UPPER(groups.name) LIKE #{query.searchSql} ESCAPE '/') </if> + <choose> + <when test="query.membership() == 'IN'"> + AND groups.permission IS NOT NULL + </when> + <when test="query.membership() == 'OUT'"> + AND groups.permission IS NULL + </when> + </choose> </where> ORDER BY groups.name </select> @@ -141,9 +149,20 @@ </where> ) groups <where> + <if test="groupName!=null"> + AND (UPPER(groups.name) LIKE #{groupName} ESCAPE '/') + </if> <if test="query.search() != null"> AND (UPPER(groups.name) LIKE #{query.searchSql} ESCAPE '/') </if> + <choose> + <when test="query.membership() == 'IN'"> + AND groups.permission IS NOT NULL + </when> + <when test="query.membership() == 'OUT'"> + AND groups.permission IS NULL + </when> + </choose> </where> </select> diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/security/DefaultGroups.java b/sonar-plugin-api/src/main/java/org/sonar/api/security/DefaultGroups.java index 2d9a4f1154c..8f07391bf7b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/security/DefaultGroups.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/security/DefaultGroups.java @@ -19,6 +19,8 @@ */ package org.sonar.api.security; +import javax.annotation.Nullable; + /** * Name of the default user groups * @@ -32,7 +34,7 @@ public final class DefaultGroups { public static final String ADMINISTRATORS = "sonar-administrators"; public static final String USERS = "sonar-users"; - public static boolean isAnyone(String groupName) { + public static boolean isAnyone(@Nullable String groupName) { return ANYONE.equalsIgnoreCase(groupName); } } |