From: Jean-Baptiste Lievremont Date: Fri, 4 Oct 2013 09:39:21 +0000 (+0200) Subject: SONAR-3871 SONAR-4709 Fix permission checks for project provisioning X-Git-Tag: 4.0~234 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=33f64654b6fa84ef1264393b8fc5b11a47601192;p=sonarqube.git SONAR-3871 SONAR-4709 Fix permission checks for project provisioning Allow users with 'provisioning' permission to apply default permission template on provisioned project Hide 'provisioning' section to users w/o 'provisioning' permission Check for 'provisioning' permission (instead of 'admin') for provisioned project manipulation --- diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index 74666be78a4..712039e149c 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -261,6 +261,18 @@ public class ResourceDao { } } + /** + * Return provisioned project with given key + */ + public ResourceDto selectProvisionedProject(String key) { + SqlSession session = mybatis.openSession(); + try { + return session.getMapper(ResourceMapper.class).selectProvisionedProject(key); + } finally { + MyBatis.closeQuietly(session); + } + } + public static ComponentDto toComponent(ResourceDto resourceDto){ return new ComponentDto() .setId(resourceDto.getId()) diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java index c3bcc36277c..82c25ad1cd0 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java @@ -82,7 +82,10 @@ public interface ResourceMapper { List selectProvisionedProjects(@Param("qualifiers") Collection qualifier); + ResourceDto selectProvisionedProject(@Param("key") String key); + void insert(ResourceDto resource); void update(ResourceDto resource); + } diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml index ffc899c4641..cc0fbcfe1bd 100644 --- a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml @@ -193,6 +193,16 @@ + + diff --git a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java index d9dd9c775e5..57a728b7c29 100644 --- a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java @@ -361,6 +361,15 @@ public class ResourceDaoTest extends AbstractDaoTestCase { assertThat(dao.selectProvisionedProjects(Collections.emptyList())).isEmpty(); } + @Test + public void should_select_provisioned_project_by_key(){ + setupData("fixture-including-ghost-projects-and-technical-project"); + + String key = "org.sample:sample"; + assertThat(dao.selectProvisionedProject(key).getKey()).isEqualTo(key); + assertThat(dao.selectProvisionedProject("unknown")).isNull(); + } + private List getKeys(final List components){ return newArrayList(Iterables.transform(components, new Function() { @Override diff --git a/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java b/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java index 44e0bd48c4c..7b5d5913b68 100644 --- a/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java +++ b/sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java @@ -20,6 +20,11 @@ package org.sonar.server.permission; +import com.google.common.base.Predicate; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; @@ -38,6 +43,7 @@ import org.sonar.server.user.UserSession; import javax.annotation.Nullable; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -78,12 +84,19 @@ public class InternalPermissionService implements ServerComponent { changePermission(REMOVE, params); } - public void applyDefaultPermissionTemplate(String componentKey) { + public void applyDefaultPermissionTemplate(final String componentKey) { UserSession.get().checkLoggedIn(); - UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); ComponentDto component = (ComponentDto) resourceDao.findByKey(componentKey); badRequestIfNullResult(component, OBJECT_TYPE_COMPONENT, componentKey); + + ResourceDto provisioned = resourceDao.selectProvisionedProject(componentKey); + if (provisioned == null) { + UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + } else { + UserSession.get().checkGlobalPermission(GlobalPermissions.PROVISIONING); + } + permissionFacade.grantDefaultRoles(component.getId(), component.qualifier()); } diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb index 78594c3277b..5b3faabdda8 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb @@ -32,7 +32,7 @@ class ProvisioningController < ApplicationController def create_or_update verify_post_request - access_denied unless is_admin? + access_denied unless has_role?("provisioning") @id = params[:id] @key = params[:key] @name = params[:name] @@ -63,7 +63,7 @@ class ProvisioningController < ApplicationController end def delete - access_denied unless is_admin? + access_denied unless has_role?("provisioning") @id = params[:id].to_i Java::OrgSonarServerUi::JRubyFacade.getInstance().deleteResourceTree(@id) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb index 2ef018270a4..c643e0d093e 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb +++ b/sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb @@ -140,9 +140,11 @@
  • <%= message('backup.page') -%>
  • + <% if has_role?("provisioning") %>
  • <%= message('provisioning.page') -%>
  • + <% end %>
  • <%= message('bulk_deletion.page') -%>
  • diff --git a/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java b/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java index 82328d827ee..ad5dc4f13dd 100644 --- a/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java @@ -26,14 +26,15 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.resources.Qualifiers; import org.sonar.api.security.DefaultGroups; +import org.sonar.core.component.ComponentDto; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.permission.PermissionFacade; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; import org.sonar.core.user.GroupDto; -import org.sonar.core.user.RoleDao; import org.sonar.core.user.UserDao; import org.sonar.core.user.UserDto; import org.sonar.server.exceptions.BadRequestException; @@ -44,7 +45,14 @@ import org.sonar.server.user.MockUserSession; import java.util.Map; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class InternalPermissionServiceTest { @@ -336,6 +344,61 @@ public class InternalPermissionServiceTest { verify(permissionFacade).applyPermissionTemplate("my_template_key", 3L); } + @Test + public void apply_default_permission_template_on_standard_project() { + final String componentKey = "component"; + final long componentId = 1234l; + final String qualifier = Qualifiers.PROJECT; + + ComponentDto mockComponent = mock(ComponentDto.class); + when(mockComponent.getId()).thenReturn(componentId); + when(mockComponent.qualifier()).thenReturn(qualifier); + + when(resourceDao.findByKey(componentKey)).thenReturn(mockComponent); + service.applyDefaultPermissionTemplate(componentKey); + verify(resourceDao).findByKey(componentKey); + verify(permissionFacade).grantDefaultRoles(componentId, qualifier); + } + + @Test(expected = ForbiddenException.class) + public void apply_default_permission_template_on_provisioned_project_without_permission() { + final String componentKey = "component"; + final long componentId = 1234l; + final String qualifier = Qualifiers.PROJECT; + + ComponentDto mockComponent = mock(ComponentDto.class); + when(mockComponent.getId()).thenReturn(componentId); + when(mockComponent.qualifier()).thenReturn(qualifier); + + when(resourceDao.findByKey(componentKey)).thenReturn(mockComponent); + when(resourceDao.selectProvisionedProject(componentKey)).thenReturn(mock(ResourceDto.class)); + service.applyDefaultPermissionTemplate(componentKey); + } + + @Test + public void apply_default_permission_template_on_provisioned_project_with_permission() { + MockUserSession.set().setLogin("provisioning").setPermissions(GlobalPermissions.PROVISIONING); + final String componentKey = "component"; + final long componentId = 1234l; + final String qualifier = Qualifiers.PROJECT; + + ComponentDto mockComponent = mock(ComponentDto.class); + when(mockComponent.getId()).thenReturn(componentId); + when(mockComponent.qualifier()).thenReturn(qualifier); + + when(resourceDao.findByKey(componentKey)).thenReturn(mockComponent); + when(resourceDao.selectProvisionedProject(componentKey)).thenReturn(mock(ResourceDto.class)); + service.applyDefaultPermissionTemplate(componentKey); + } + + @Test(expected = BadRequestException.class) + public void apply_default_permission_template_on_non_existing_project() { + final String componentKey = "component"; + + when(resourceDao.findByKey(componentKey)).thenReturn(null); + service.applyDefaultPermissionTemplate(componentKey); + } + private Map buildPermissionChangeParams(String login, String group, String permission) { Map params = Maps.newHashMap(); params.put("user", login);