]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3871 SONAR-4709 Fix permission checks for project provisioning
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Fri, 4 Oct 2013 09:39:21 +0000 (11:39 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Fri, 4 Oct 2013 09:50:56 +0000 (11:50 +0200)
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

sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java
sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java
sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml
sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java
sonar-server/src/main/java/org/sonar/server/permission/InternalPermissionService.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb
sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionServiceTest.java

index 74666be78a49a63874c9ac6030929353f143b99f..712039e149c1b0e58bc67d6c6722d831a342aead 100644 (file)
@@ -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())
index c3bcc36277ce12c82c8bf6eba3913c68a8b9fe91..82c25ad1cd049b77c1d0c548d73dbf0b791b80df 100644 (file)
@@ -82,7 +82,10 @@ public interface ResourceMapper {
 
   List<ResourceDto> selectProvisionedProjects(@Param("qualifiers") Collection<String> qualifier);
 
+  ResourceDto selectProvisionedProject(@Param("key") String key);
+
   void insert(ResourceDto resource);
 
   void update(ResourceDto resource);
+
 }
index ffc899c4641ff8b3b165f0ed6002718950dddf55..cc0fbcfe1bdfe0175492adf11fa7101c9ed89b67 100644 (file)
     </where>
   </select>
 
+  <select id="selectProvisionedProject" parameterType="string" resultMap="resourceResultMap">
+    select p.* from projects p
+    left join snapshots s on s.project_id=p.id
+    <where>
+      and s.id is null
+      and p.kee = #{key}
+      and p.copy_resource_id is null
+    </where>
+  </select>
+
   <select id="selectAuthorizedChildrenComponentIds" parameterType="map" resultType="int">
     <include refid="selectAuthorizedChildrenComponentIdsQuery" />
   </select>
index d9dd9c775e5e5856014239e0a6b084bc9bba1489..57a728b7c29198dac58ca246b0742f5de9288ce4 100644 (file)
@@ -361,6 +361,15 @@ public class ResourceDaoTest extends AbstractDaoTestCase {
     assertThat(dao.selectProvisionedProjects(Collections.<String>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<String> getKeys(final List<Component> components){
     return newArrayList(Iterables.transform(components, new Function<Component, String>() {
       @Override
index 44e0bd48c4ce494b5ebebc80c0039a64c3fd6d0d..7b5d5913b683e3933619db51c1535796d9b5805c 100644 (file)
 
 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());
   }
 
index 78594c3277bd7d8e7649a5c231469c99730d1e3c..5b3faabdda89ccae9c9ba40ec0d576abeceabaec 100644 (file)
@@ -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)
index 2ef018270a40d7792d07fc252cd3cf0a037face8..c643e0d093e5eab4d5068154e5295f410d65f5dd 100644 (file)
             <li class="sidebar-title"><%= message('sidebar.system') -%></li>
             <li class="<%= 'active' if controller.controller_path=='backup' -%>"><a href="<%= ApplicationController.root_context -%>/backup"><%= message('backup.page') -%></a>
             </li>
+            <% if has_role?("provisioning") %>
             <li class="<%= 'active' if controller.controller_path=='provisioning' -%>">
               <a href="<%= ApplicationController.root_context -%>/provisioning"><%= message('provisioning.page') -%></a>
             </li>
+            <% end %>
             <li class="<%= 'active' if controller.controller_path=='bulk_deletion' -%>">
               <a href="<%= ApplicationController.root_context -%>/bulk_deletion"><%= message('bulk_deletion.page') -%></a>
             </li>
index 82328d827ee47b6c71d63835acbaab0aec6e7e4c..ad5dc4f13ddace6f42959e2f66a2217279e9659b 100644 (file)
@@ -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<String, Object> buildPermissionChangeParams(String login, String group, String permission) {
     Map<String, Object> params = Maps.newHashMap();
     params.put("user", login);