return mapper(session).selectModuleFilesTree(rootComponentUuid, Scopes.FILE);
}
+ public List<ComponentDto> getByIds(final DbSession session, Collection<Long> ids) {
+ return DaoUtils.executeLargeInputs(ids, new Function<List<Long>, List<ComponentDto>>() {
+ @Override
+ public List<ComponentDto> apply(List<Long> partition) {
+ return mapper(session).findByIds(partition);
+ }
+ });
+ }
+
public List<ComponentDto> getByUuids(final DbSession session, Collection<String> uuids) {
return DaoUtils.executeLargeInputs(uuids, new Function<List<String>, List<ComponentDto>>() {
@Override
public List<String> selectProjectsFromView(DbSession session, String viewUuid, String projectViewUuid) {
return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid);
}
+
}
--- /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.component.db;
+
+import org.sonar.api.ServerComponent;
+import org.sonar.core.component.db.ComponentIndexMapper;
+import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DbSession;
+
+import java.util.List;
+
+public class ComponentIndexDao implements ServerComponent, DaoComponent {
+
+ public List<Long> selectProjectIdsFromQueryAndViewOrSubViewUuid(DbSession session, String query, String viewOrSubViewUuid) {
+ return session.getMapper(ComponentIndexMapper.class).selectProjectIdsFromQueryAndViewOrSubViewUuid(query + "%", "%." + viewOrSubViewUuid + ".%");
+ }
+
+}
public class ComponentsWs implements WebService {
private final ComponentAppAction appAction;
+ private final SearchAction searchAction;
- public ComponentsWs(ComponentAppAction appAction) {
+ public ComponentsWs(ComponentAppAction appAction, SearchAction searchAction) {
this.appAction = appAction;
+ this.searchAction = searchAction;
}
@Override
.setDescription("Components management");
appAction.define(controller);
+ searchAction.define(controller);
defineSuggestionsAction(controller);
controller.done();
--- /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.component.ws;
+
+import com.google.common.collect.Sets;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.user.UserSession;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newLinkedHashSet;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+
+public class SearchAction implements RequestHandler {
+
+ private static final short MINIMUM_SEARCH_CHARACTERS = 2;
+
+ private static final String PARAM_COMPONENT_UUID = "componentUuid";
+ private static final String PARAM_QUERY = "q";
+
+ private final DbClient dbClient;
+
+ public SearchAction(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("search")
+ .setDescription("Search for components. Currently limited to projects in a view or a sub-view")
+ .setSince("5.1")
+ .setInternal(true)
+ .setHandler(this);
+
+ action
+ .createParam(PARAM_COMPONENT_UUID)
+ .setRequired(true)
+ .setDescription("View or sub view UUID")
+ .setExampleValue("d6d9e1e5-5e13-44fa-ab82-3ec29efa8935");
+
+ action
+ .createParam(PARAM_QUERY)
+ .setRequired(true)
+ .setDescription("UTF-8 search query")
+ .setExampleValue("sonar");
+
+ action.addPagingParams(10);
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+ String query = request.mandatoryParam(PARAM_QUERY);
+ if (query.length() < MINIMUM_SEARCH_CHARACTERS) {
+ throw new IllegalArgumentException(String.format("Minimum search is %s characters", MINIMUM_SEARCH_CHARACTERS));
+ }
+ String viewOrSubUuid = request.mandatoryParam(PARAM_COMPONENT_UUID);
+
+ JsonWriter json = response.newJsonWriter();
+ json.beginObject();
+
+ DbSession session = dbClient.openSession(false);
+ try {
+ ComponentDto componentDto = dbClient.componentDao().getByUuid(session, viewOrSubUuid);
+ UserSession.get().checkProjectUuidPermission(UserRole.USER, componentDto.projectUuid());
+
+ Set<Long> projectIds = newLinkedHashSet(dbClient.componentIndexDao().selectProjectIdsFromQueryAndViewOrSubViewUuid(session, query, componentDto.uuid()));
+ Collection<Long> authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(session, projectIds, UserSession.get().userId(), UserRole.USER);
+
+ SearchOptions options = new SearchOptions();
+ options.setPage(request.mandatoryParamAsInt(PAGE), request.mandatoryParamAsInt(PAGE_SIZE));
+ Set<Long> pagedProjectIds = pagedProjectIds(authorizedProjectIds, options);
+
+ List<ComponentDto> projects = dbClient.componentDao().getByIds(session, pagedProjectIds);
+
+ options.writeJson(json, authorizedProjectIds.size());
+ json.name("components").beginArray();
+ for (ComponentDto project : projects) {
+ json.beginObject();
+ json.prop("uuid", project.uuid());
+ json.prop("name", project.name());
+ json.endObject();
+ }
+ json.endArray();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+
+ json.endObject();
+ json.close();
+ }
+
+ private Set<Long> pagedProjectIds(Collection<Long> projectIds, SearchOptions options) {
+ Set<Long> results = Sets.newLinkedHashSet();
+ int index = 0;
+ for (Long projectId : projectIds) {
+ if (index >= options.getOffset() && results.size() < options.getLimit()) {
+ results.add(projectId);
+ } else if (results.size() >= options.getLimit()) {
+ break;
+ }
+ index++;
+ }
+ return results;
+ }
+}
import org.sonar.core.user.AuthorizationDao;
import org.sonar.server.activity.db.ActivityDao;
import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.computation.db.AnalysisReportDao;
import org.sonar.server.dashboard.db.DashboardDao;
private final WidgetPropertyDao widgetPropertyDao;
private final FileSourceDao fileSourceDao;
private final AuthorDao authorDao;
+ private final ComponentIndexDao componentIndexDao;
public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) {
this.db = db;
widgetPropertyDao = getDao(map, WidgetPropertyDao.class);
fileSourceDao = getDao(map, FileSourceDao.class);
authorDao = getDao(map, AuthorDao.class);
+ componentIndexDao = getDao(map, ComponentIndexDao.class);
}
public Database database() {
return authorDao;
}
+ public ComponentIndexDao componentIndexDao() {
+ return componentIndexDao;
+ }
+
private <K> K getDao(Map<Class, DaoComponent> map, Class<K> clazz) {
return (K) map.get(clazz);
}
import org.sonar.server.component.DefaultComponentFinder;
import org.sonar.server.component.DefaultRubyComponentService;
import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.component.ws.*;
import org.sonar.server.computation.AnalysisReportQueue;
MetricDao.class,
ComponentDao.class,
SnapshotDao.class,
- DbClient.class,
MeasureFilterDao.class,
AnalysisReportDao.class,
+ ComponentIndexDao.class,
+ DbClient.class,
// Elasticsearch
SearchClient.class,
pico.addSingleton(DefaultComponentFinder.class);
pico.addSingleton(DefaultRubyComponentService.class);
pico.addSingleton(ComponentService.class);
- pico.addSingleton(ComponentDao.class);
pico.addSingleton(ResourcesWs.class);
pico.addSingleton(ComponentsWs.class);
pico.addSingleton(ProjectsWs.class);
pico.addSingleton(ComponentAppAction.class);
+ pico.addSingleton(org.sonar.server.component.ws.SearchAction.class);
pico.addSingleton(EventsWs.class);
pico.addSingleton(ComponentCleanerService.class);
return this;
}
+ /**
+ * Ensures that user implies the specified project permission. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown.
+ */
+ public UserSession checkProjectUuidPermission(String projectPermission, String projectUuid) {
+ if (!hasProjectPermissionByUuid(projectPermission, projectUuid)) {
+ throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
+ }
+ return this;
+ }
+
/**
* Does the user have the given project permission ?
*/
}
/**
- * Does the user have the given project permission for a component ?
+ * Does the user have the given project permission for a component key ?
*/
public boolean hasComponentPermission(String permission, String componentKey) {
String projectKey = projectKeyByComponentKey.get(componentKey);
if (projectKey == null) {
- // TODO use method using UUID
ResourceDto project = resourceDao().getRootProjectByComponentKey(componentKey);
if (project == null) {
return false;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.experimental.categories.Category;
import org.sonar.api.platform.Server;
import org.sonar.api.web.UserRole;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.server.issue.db.IssueDao;
import org.sonar.server.user.MockUserSession;
import org.sonar.server.ws.WsTester;
+import org.sonar.test.DbTests;
import static org.mockito.Mockito.mock;
+@Category(DbTests.class)
public class IssuesActionTest {
private final static String PROJECT_KEY = "struts";
assertThat(dao.getByKeys(session, "unknown")).isEmpty();
}
+ @Test
+ public void get_by_ids() {
+ setupData("shared");
+
+ List<ComponentDto> results = dao.getByIds(session, newArrayList(4L));
+ assertThat(results).hasSize(1);
+
+ ComponentDto result = results.get(0);
+ assertThat(result).isNotNull();
+ assertThat(result.key()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java");
+ assertThat(result.path()).isEqualTo("src/org/struts/RequestContext.java");
+ assertThat(result.name()).isEqualTo("RequestContext.java");
+ assertThat(result.longName()).isEqualTo("org.struts.RequestContext");
+ assertThat(result.qualifier()).isEqualTo("FIL");
+ assertThat(result.scope()).isEqualTo("FIL");
+ assertThat(result.language()).isEqualTo("java");
+ assertThat(result.parentProjectId()).isEqualTo(2);
+
+ assertThat(dao.getByIds(session, newArrayList(555L))).isEmpty();
+ }
+
@Test
public void get_by_uuids() {
setupData("shared");
--- /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.component.db;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class ComponentIndexDaoTest {
+
+ @Rule
+ public DbTester dbTester = new DbTester();
+
+ DbSession session;
+
+ ComponentIndexDao dao;
+
+ @Before
+ public void createDao() throws Exception {
+ session = dbTester.myBatis().openSession(false);
+ dao = new ComponentIndexDao();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ session.close();
+ }
+
+ @Test
+ public void select_project_ids_from_query_and_view_or_sub_view_uuid() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "select_project_ids_from_query_and_view_or_sub_view_uuid.xml");
+ String viewUuid = "EFGH";
+
+ assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "project", viewUuid)).containsOnly(1L, 2L);
+ assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "one", viewUuid)).containsOnly(1L);
+ assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "two", viewUuid)).containsOnly(2L);
+ assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "unknown", viewUuid)).isEmpty();
+ }
+}
when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures);
- tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n)));
+ tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n), mock(SearchAction.class)));
}
@Test
@Before
public void setUp() throws Exception {
- WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class))));
+ WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class)), new SearchAction(mock(DbClient.class))));
controller = tester.controller("api/components");
}
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("4.2");
- assertThat(controller.actions()).hasSize(2);
+ assertThat(controller.actions()).hasSize(3);
}
@Test
assertThat(action.params()).hasSize(2);
}
+ @Test
+ public void define_search_action() throws Exception {
+ WebService.Action action = controller.action("search");
+ assertThat(action).isNotNull();
+ assertThat(action.isInternal()).isTrue();
+ assertThat(action.isPost()).isFalse();
+ assertThat(action.handler()).isNotNull();
+ assertThat(action.params()).hasSize(4);
+ }
+
}
--- /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.component.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.AuthorizationDao;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.ComponentIndexDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+import static org.mockito.Mockito.mock;
+
+public class SearchActionMediumTest {
+
+ @Rule
+ public DbTester dbTester = new DbTester();
+
+ WsTester tester;
+
+ @Before
+ public void setUp() throws Exception {
+ DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(),
+ new ComponentDao(), new AuthorizationDao(dbTester.myBatis()), new ComponentIndexDao()
+ );
+ tester = new WsTester(new ComponentsWs(mock(ComponentAppAction.class), new SearchAction(dbClient)));
+ }
+
+ @Test
+ public void return_projects_from_view() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+ MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st");
+ request.execute().assertJson(getClass(), "return_projects_from_view.json");
+ }
+
+ @Test
+ public void return_projects_from_subview() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+ MockUserSession.set().setLogin("john").addComponentUuidPermission(UserRole.USER, "EFGH", "FGHI");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "FGHI").setParam("q", "st");
+ request.execute().assertJson(getClass(), "return_projects_from_subview.json");
+ }
+
+ @Test
+ public void return_only_authorized_projects_from_view() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "return_only_authorized_projects_from_view.xml");
+ MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st");
+ request.execute().assertJson(getClass(), "return_only_authorized_projects_from_view.json");
+ }
+
+ @Test
+ public void return_paged_result() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+ MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "2").setParam("ps", "1");
+ request.execute().assertJson(getClass(), "return_paged_result.json", false);
+ }
+
+ @Test
+ public void return_only_first_page() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+ MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "1").setParam("ps", "1");
+ request.execute().assertJson(getClass(), "return_only_first_page.json", false);
+ }
+
+ @Test
+ public void fail_when_search_param_is_too_short() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+ MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
+
+ WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "s");
+
+ try {
+ request.execute();
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Minimum search is 2 characters");
+ }
+ }
+}
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;
import org.sonar.core.user.AuthorizationDao;
+import org.sonar.server.component.ComponentTesting;
import org.sonar.server.exceptions.ForbiddenException;
import javax.annotation.Nullable;
session.checkProjectPermission(UserRole.USER, "com.foo:Bar");
}
+ @Test
+ public void check_project_uuid_permission_ok() throws Exception {
+ AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+ UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1);
+
+ ComponentDto project = ComponentTesting.newProjectDto();
+ when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+ session.checkProjectUuidPermission(UserRole.USER, project.uuid());
+ }
+
+ @Test(expected = ForbiddenException.class)
+ public void check_project_uuid_permission_ko() throws Exception {
+ AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+ UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1);
+
+ ComponentDto project = ComponentTesting.newProjectDto();
+ when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+ session.checkProjectUuidPermission(UserRole.USER, "another project");
+ }
+
@Test
public void has_component_permission() throws Exception {
AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
}
@Test
- public void check_component_permission_ok() throws Exception {
+ public void check_component_key_permission_ok() throws Exception {
AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
ResourceDao resourceDao = mock(ResourceDao.class);
UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
}
@Test(expected = ForbiddenException.class)
- public void check_component_permission_ko() throws Exception {
+ public void check_component_key_permission_ko() throws Exception {
AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
ResourceDao resourceDao = mock(ResourceDao.class);
UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
}
@Test(expected = ForbiddenException.class)
- public void check_component_permission_when_project_not_found() throws Exception {
+ public void check_component_key_permission_when_project_not_found() throws Exception {
AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
ResourceDao resourceDao = mock(ResourceDao.class);
UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
session.checkComponentPermission(UserRole.USER, "com.foo:Bar:BarFile.xoo");
}
+ @Test(expected = ForbiddenException.class)
+ public void check_component_dto_permission_ko() throws Exception {
+ AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
+ ResourceDao resourceDao = mock(ResourceDao.class);
+ UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1);
+
+ ComponentDto project = ComponentTesting.newProjectDto();
+ when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(project.uuid()));
+
+ session.checkComponentPermission(UserRole.USER, "another");
+ }
+
static class SpyUserSession extends UserSession {
private AuthorizationDao authorizationDao;
private ResourceDao resourceDao;
--- /dev/null
+<dataset>
+
+ <!-- Real projects -->
+ <projects id="1" uuid="ABCD" project_uuid="ABCD" module_uuid_path=".ABCD." copy_resource_id="[null]" name="Project One" qualifier="TRK" scope="PRJ"/>
+ <projects id="2" uuid="BCDE" project_uuid="BCDE" module_uuid_path=".BCDE." copy_resource_id="[null]" name="Project Two" qualifier="TRK" scope="PRJ"/>
+
+ <!-- Copy projects -->
+ <projects id="3" uuid="CDEF" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="1" name="Copy Project One" qualifier="TRK" scope="FIL"/>
+ <projects id="4" uuid="DEFG" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="2" name="Copy Project One" qualifier="TRK" scope="FIL"/>
+
+ <!-- View containing all projects -->
+ <projects id="5" uuid="EFGH" project_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="[null]" name="All projects" qualifier="VW" scope="PRJ"/>
+
+ <resource_index id="1" kee="project one" resource_id="1" root_project_id="1" position="0" name_size="11" qualifier="TRK"/>
+ <resource_index id="2" kee="roject one" resource_id="1" root_project_id="1" position="1" name_size="11" qualifier="TRK"/>
+ <resource_index id="3" kee="oject one" resource_id="1" root_project_id="1" position="2" name_size="11" qualifier="TRK"/>
+ <resource_index id="4" kee="ject one" resource_id="1" root_project_id="1" position="3" name_size="11" qualifier="TRK"/>
+ <resource_index id="5" kee="ect one" resource_id="1" root_project_id="1" position="4" name_size="11" qualifier="TRK"/>
+ <resource_index id="6" kee="ct one" resource_id="1" root_project_id="1" position="5" name_size="11" qualifier="TRK"/>
+ <resource_index id="7" kee="t one" resource_id="1" root_project_id="1" position="6" name_size="11" qualifier="TRK"/>
+ <resource_index id="8" kee=" one" resource_id="1" root_project_id="1" position="7" name_size="11" qualifier="TRK"/>
+ <resource_index id="9" kee="one" resource_id="1" root_project_id="1" position="8" name_size="11" qualifier="TRK"/>
+ <resource_index id="10" kee="project two" resource_id="2" root_project_id="2" position="0" name_size="11" qualifier="TRK"/>
+ <resource_index id="11" kee="roject two" resource_id="2" root_project_id="2" position="1" name_size="11" qualifier="TRK"/>
+ <resource_index id="12" kee="oject two" resource_id="2" root_project_id="2" position="2" name_size="11" qualifier="TRK"/>
+ <resource_index id="13" kee="ject two" resource_id="2" root_project_id="2" position="3" name_size="11" qualifier="TRK"/>
+ <resource_index id="14" kee="ect two" resource_id="2" root_project_id="2" position="4" name_size="11" qualifier="TRK"/>
+ <resource_index id="15" kee="ct two" resource_id="2" root_project_id="2" position="5" name_size="11" qualifier="TRK"/>
+ <resource_index id="16" kee="t two" resource_id="2" root_project_id="2" position="6" name_size="11" qualifier="TRK"/>
+ <resource_index id="17" kee="two" resource_id="2" root_project_id="2" position="7" name_size="11" qualifier="TRK"/>
+
+</dataset>
--- /dev/null
+{
+ "total": 1,
+ "p": 1,
+ "ps": 10,
+ "components": [
+ {
+ "uuid": "JKLM",
+ "name": "Struts"
+ }
+ ]
+}
--- /dev/null
+<dataset>
+
+ <!-- Only struts is authorized for all user -->
+ <group_roles id="1" group_id="[null]" resource_id="100" role="user"/>
+
+ <!-- View -->
+ <projects id="11" uuid="EFGH" project_uuid="EFGH" module_uuid="[null]" module_uuid_path="." copy_resource_id="[null]" enabled="[true]"
+ kee="LANGUAGE_VIEW" scope="PRJ" qualifier="VW" name="By Language" path="[null]"/>
+
+ <projects id="112" uuid="GHIJ" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="101" enabled="[true]"
+ kee="VIEW2org.elasticsearch:elasticsearch" scope="FIL" qualifier="TRK" name="Elasticsearch" path="[null]"/>
+
+ <projects id="113" uuid="HIJK" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="100" enabled="[true]"
+ kee="VIEW2org.struts:struts" scope="FIL" qualifier="TRK" name="Struts" path="[null]"/>
+
+ <!-- Real projects -->
+
+ <projects id="100" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="JKLM" project_uuid="JKLM" module_uuid="[null]" module_uuid_path="."
+ enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+ <projects id="101" scope="PRJ" qualifier="TRK" kee="org.elasticsearch:elasticsearch" name="Elasticsearch"
+ uuid="KLMN" project_uuid="KLMN" module_uuid="[null]" module_uuid_path="."
+ enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+ <resource_index id="1" kee="struts" resource_id="100" root_project_id="100" position="0" name_size="6" qualifier="TRK"/>
+ <resource_index id="2" kee="elasticsearch" resource_id="101" root_project_id="1" position="0" name_size="13" qualifier="TRK"/>
+ <resource_index id="3" kee="sticsearch" resource_id="101" root_project_id="1" position="1" name_size="13" qualifier="TRK"/>
+
+</dataset>
--- /dev/null
+{
+ "total": 2,
+ "p": 1,
+ "ps": 1
+}
--- /dev/null
+{
+ "total": 2,
+ "p": 2,
+ "ps": 1
+}
--- /dev/null
+{
+ "total": 1,
+ "p": 1,
+ "ps": 10,
+ "components": [
+ {
+ "uuid": "JKLM",
+ "name": "Struts"
+ }
+ ]
+}
--- /dev/null
+{
+ "total": 2,
+ "p": 1,
+ "ps": 10,
+ "components": [
+ {
+ "uuid": "JKLM",
+ "name": "Struts"
+ },
+ {
+ "uuid": "KLMN",
+ "name": "Elasticsearch"
+ }
+ ]
+}
--- /dev/null
+<dataset>
+
+ <!-- Projects is authorized for all user -->
+ <group_roles id="1" group_id="[null]" resource_id="100" role="user"/>
+ <group_roles id="2" group_id="[null]" resource_id="101" role="user"/>
+
+ <!-- View with sub view -->
+ <projects id="11" uuid="EFGH" project_uuid="EFGH" module_uuid="[null]" module_uuid_path="." copy_resource_id="[null]" enabled="[true]"
+ kee="LANGUAGE_VIEW" scope="PRJ" qualifier="VW" name="By Language" path="[null]"/>
+
+ <projects id="112" uuid="GHIJ" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="101" enabled="[true]"
+ kee="VIEW2org.elasticsearch:elasticsearch" scope="FIL" qualifier="TRK" name="Elasticsearch" path="[null]"/>
+
+ <!-- Sub view -->
+ <projects id="13" uuid="FGHI" project_uuid="EFGH" module_uuid="EFGH" module_uuid_path=".EFGH." copy_resource_id="[null]" enabled="[true]"
+ kee="JAVA_PROJECTS" scope="PRJ" qualifier="SVW" name="Java projects" path="[null]"/>
+
+ <projects id="113" uuid="HIJK" project_uuid="EFGH" module_uuid="FGHI" module_uuid_path=".EFGH.FGHI." copy_resource_id="100" enabled="[true]"
+ kee="VIEW2org.struts:struts" scope="FIL" qualifier="TRK" name="Struts" path="[null]"/>
+
+ <!-- Real projects -->
+
+ <projects id="100" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="JKLM" project_uuid="JKLM" module_uuid="[null]" module_uuid_path="."
+ enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+ <projects id="101" scope="PRJ" qualifier="TRK" kee="org.elasticsearch:elasticsearch" name="Elasticsearch"
+ uuid="KLMN" project_uuid="KLMN" module_uuid="[null]" module_uuid_path="."
+ enabled="[true]" copy_resource_id="[null]" path="[null]"/>
+
+ <resource_index id="1" kee="struts" resource_id="100" root_project_id="100" position="0" name_size="6" qualifier="TRK"/>
+ <resource_index id="2" kee="elasticsearch" resource_id="101" root_project_id="1" position="0" name_size="13" qualifier="TRK"/>
+ <resource_index id="3" kee="sticsearch" resource_id="101" root_project_id="1" position="1" name_size="13" qualifier="TRK"/>
+
+</dataset>
}
/**
- * Return the root project id. On a root project, return itself
+ * Return the root project uuid. On a root project, return itself
*/
public String projectUuid() {
return projectUuid;
--- /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.core.component.db;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface ComponentIndexMapper {
+
+ List<Long> selectProjectIdsFromQueryAndViewOrSubViewUuid(@Param("query") String query, @Param("viewOrSubViewUuid") String viewOrSubViewUuid);
+}
*/
public interface ComponentMapper {
- /**
- * Warning, projectId is always null
- */
@CheckForNull
ComponentDto selectByKey(String key);
- /**
- * Warning, projectId is always null
- */
@CheckForNull
ComponentDto selectById(long id);
- /**
- * Warning, projectId is always null
- */
@CheckForNull
ComponentDto selectByUuid(String uuid);
List<ComponentDto> findByKeys(@Param("keys") Collection<String> keys);
+ List<ComponentDto> findByIds(@Param("ids") Collection<Long> ids);
+
List<ComponentDto> findByUuids(@Param("uuids") Collection<String> uuids);
List<String> selectExistingUuids(@Param("uuids") Collection<String> uuids);
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.Environment;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.ExecutorType;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.session.*;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.ibatis.type.JdbcType;
import org.slf4j.LoggerFactory;
import org.sonar.core.component.FilePathWithHashDto;
import org.sonar.core.component.SnapshotDto;
import org.sonar.core.component.UuidWithProjectUuidDto;
+import org.sonar.core.component.db.ComponentIndexMapper;
import org.sonar.core.component.db.ComponentMapper;
import org.sonar.core.component.db.SnapshotMapper;
import org.sonar.core.computation.db.AnalysisReportDto;
import org.sonar.core.computation.db.AnalysisReportMapper;
import org.sonar.core.config.Logback;
-import org.sonar.core.dashboard.ActiveDashboardDto;
-import org.sonar.core.dashboard.ActiveDashboardMapper;
-import org.sonar.core.dashboard.DashboardDto;
-import org.sonar.core.dashboard.DashboardMapper;
-import org.sonar.core.dashboard.WidgetDto;
-import org.sonar.core.dashboard.WidgetMapper;
-import org.sonar.core.dashboard.WidgetPropertyDto;
-import org.sonar.core.dashboard.WidgetPropertyMapper;
+import org.sonar.core.dashboard.*;
import org.sonar.core.dependency.DependencyDto;
import org.sonar.core.dependency.DependencyMapper;
import org.sonar.core.dependency.ResourceSnapshotDto;
import org.sonar.core.duplication.DuplicationUnitDto;
import org.sonar.core.graph.jdbc.GraphDto;
import org.sonar.core.graph.jdbc.GraphDtoMapper;
-import org.sonar.core.issue.db.ActionPlanDto;
-import org.sonar.core.issue.db.ActionPlanMapper;
-import org.sonar.core.issue.db.ActionPlanStatsDto;
-import org.sonar.core.issue.db.ActionPlanStatsMapper;
-import org.sonar.core.issue.db.BatchIssueDto;
-import org.sonar.core.issue.db.IssueChangeDto;
-import org.sonar.core.issue.db.IssueChangeMapper;
-import org.sonar.core.issue.db.IssueDto;
-import org.sonar.core.issue.db.IssueFilterDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteMapper;
-import org.sonar.core.issue.db.IssueFilterMapper;
-import org.sonar.core.issue.db.IssueMapper;
-import org.sonar.core.measure.db.MeasureDto;
-import org.sonar.core.measure.db.MeasureFilterDto;
-import org.sonar.core.measure.db.MeasureFilterMapper;
-import org.sonar.core.measure.db.MeasureMapper;
-import org.sonar.core.measure.db.MetricDto;
-import org.sonar.core.measure.db.MetricMapper;
+import org.sonar.core.issue.db.*;
+import org.sonar.core.measure.db.*;
import org.sonar.core.notification.db.NotificationQueueDto;
import org.sonar.core.notification.db.NotificationQueueMapper;
-import org.sonar.core.permission.GroupWithPermissionDto;
-import org.sonar.core.permission.PermissionTemplateDto;
-import org.sonar.core.permission.PermissionTemplateGroupDto;
-import org.sonar.core.permission.PermissionTemplateMapper;
-import org.sonar.core.permission.PermissionTemplateUserDto;
-import org.sonar.core.permission.UserWithPermissionDto;
+import org.sonar.core.permission.*;
import org.sonar.core.persistence.dialect.Dialect;
import org.sonar.core.persistence.migration.v44.Migration44Mapper;
import org.sonar.core.persistence.migration.v45.Migration45Mapper;
import org.sonar.core.purge.IdUuidPair;
import org.sonar.core.purge.PurgeMapper;
import org.sonar.core.purge.PurgeableSnapshotDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper;
-import org.sonar.core.qualitygate.db.QualityGateConditionDto;
-import org.sonar.core.qualitygate.db.QualityGateConditionMapper;
-import org.sonar.core.qualitygate.db.QualityGateDto;
-import org.sonar.core.qualitygate.db.QualityGateMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleDto;
-import org.sonar.core.qualityprofile.db.ActiveRuleMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
-import org.sonar.core.qualityprofile.db.QualityProfileDto;
-import org.sonar.core.qualityprofile.db.QualityProfileMapper;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceIndexDto;
-import org.sonar.core.resource.ResourceIndexerMapper;
-import org.sonar.core.resource.ResourceKeyUpdaterMapper;
-import org.sonar.core.resource.ResourceMapper;
+import org.sonar.core.qualitygate.db.*;
+import org.sonar.core.qualityprofile.db.*;
+import org.sonar.core.resource.*;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.rule.RuleMapper;
import org.sonar.core.rule.RuleParamDto;
import org.sonar.core.technicaldebt.db.RequirementMigrationDto;
import org.sonar.core.template.LoadedTemplateDto;
import org.sonar.core.template.LoadedTemplateMapper;
-import org.sonar.core.user.AuthorDto;
-import org.sonar.core.user.AuthorMapper;
-import org.sonar.core.user.GroupDto;
-import org.sonar.core.user.GroupMapper;
-import org.sonar.core.user.GroupMembershipDto;
-import org.sonar.core.user.GroupMembershipMapper;
-import org.sonar.core.user.GroupRoleDto;
-import org.sonar.core.user.RoleMapper;
-import org.sonar.core.user.UserDto;
-import org.sonar.core.user.UserGroupDto;
-import org.sonar.core.user.UserGroupMapper;
-import org.sonar.core.user.UserMapper;
-import org.sonar.core.user.UserRoleDto;
+import org.sonar.core.user.*;
import javax.annotation.Nullable;
GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class,
MeasureMapper.class, MetricMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class,
ProjectQgateAssociationMapper.class,
- AnalysisReportMapper.class,
+ AnalysisReportMapper.class, ComponentIndexMapper.class,
Migration45Mapper.class, Migration50Mapper.class
};
loadMappers(conf, mappers);
*/
package org.sonar.core.user;
-import com.google.common.collect.ImmutableMap;
+import com.google.common.base.Function;
import com.google.common.collect.Sets;
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.ServerComponent;
import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DaoUtils;
+import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import javax.annotation.Nullable;
+
import java.util.*;
import static com.google.common.collect.Maps.newHashMap;
this.mybatis = mybatis;
}
- public Set<String> keepAuthorizedComponentKeys(Set<String> componentKeys, @Nullable Integer userId, String role) {
- SqlSession session = mybatis.openSession(false);
- try {
- return keepAuthorizedComponentKeys(componentKeys, userId, role, session);
+ public Collection<Long> keepAuthorizedProjectIds(final DbSession session, final Collection<Long> componentIds, @Nullable final Integer userId, final String role) {
+ if (componentIds.isEmpty()) {
+ return Collections.emptySet();
+ }
+ return DaoUtils.executeLargeInputs(componentIds, new Function<List<Long>, List<Long>>() {
+ @Override
+ public List<Long> apply(List<Long> partition) {
+ if (userId == null) {
+ return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForAnonymous(role, componentIds);
+ } else {
+ return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForUser(userId, role, componentIds);
+ }
+ }
+ });
+ }
+ /**
+ * Used by the Views Plugin
+ */
+ public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) {
+ DbSession session = mybatis.openSession(false);
+ try {
+ return keepAuthorizedComponentKeys(session, Sets.newHashSet(componentKey), userId, role).size() == 1;
} finally {
MyBatis.closeQuietly(session);
}
}
- public Set<String> keepAuthorizedComponentKeys(Set<String> componentKeys, @Nullable Integer userId, String role, SqlSession session) {
+ private Set<String> keepAuthorizedComponentKeys(final DbSession session, final Set<String> componentKeys, @Nullable final Integer userId, final String role) {
if (componentKeys.isEmpty()) {
return Collections.emptySet();
}
- String sql;
- Map<String, Object> params;
- if (userId == null) {
- sql = "keepAuthorizedComponentKeysForAnonymous";
- params = ImmutableMap.of("role", role, "componentKeys", componentKeys);
- } else {
- sql = "keepAuthorizedComponentKeysForUser";
- params = ImmutableMap.of(USER_ID_PARAM, userId, "role", role, "componentKeys", componentKeys);
- }
-
- return Sets.newHashSet(session.<String>selectList(sql, params));
- }
-
- /**
- * Used by the Views Plugin
- */
- public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) {
- return keepAuthorizedComponentKeys(Sets.newHashSet(componentKey), userId, role).size() == 1;
+ return Sets.newHashSet(DaoUtils.executeLargeInputs(componentKeys, new Function<List<String>, List<String>>() {
+ @Override
+ public List<String> apply(List<String> partition) {
+ if (userId == null) {
+ return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForAnonymous(role, componentKeys);
+ } else {
+ return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForUser(userId, role, componentKeys);
+ }
+ }
+ }));
}
public Collection<String> selectAuthorizedRootProjectsKeys(@Nullable Integer userId, String role) {
--- /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.core.user;
+
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+public interface AuthorizationMapper {
+
+ List<Long> keepAuthorizedProjectIdsForAnonymous(@Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
+
+ List<Long> keepAuthorizedProjectIdsForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
+
+ List<String> keepAuthorizedComponentKeysForAnonymous(@Param("role") String role, @Param("componentKeys") Collection<String> componentKeys);
+
+ List<String> keepAuthorizedComponentKeysForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentKeys") Collection<String> componentKeys);
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.sonar.core.component.db.ComponentIndexMapper">
+
+ <select id="selectProjectIdsFromQueryAndViewOrSubViewUuid" parameterType="map" resultType="long">
+ SELECT r.resource_id FROM resource_index r
+ INNER JOIN projects copy ON copy.copy_resource_id = r.resource_id
+ <where>
+ AND copy.module_uuid_path LIKE #{viewOrSubViewUuid}
+ AND r.kee LIKE #{query}
+ </where>
+ ORDER BY r.name_size
+ </select>
+
+</mapper>
+
</where>
</select>
+ <select id="findByIds" parameterType="long" resultType="Component">
+ select
+ <include refid="componentColumns"/>
+ from projects p
+ <where>
+ p.enabled=${_true}
+ and p.id in
+ <foreach collection="ids" open="(" close=")" item="id" separator=",">
+ #{id}
+ </foreach>
+ </where>
+ </select>
+
<select id="findByUuids" parameterType="String" resultType="Component">
select <include refid="componentColumns"/>
from projects p
<foreach collection="componentKeys" open="(" close=")" item="element" index="index" separator=" or " >p.kee=#{element}</foreach>
</select>
+ <select id="keepAuthorizedProjectIdsForUser" parameterType="map" resultType="long">
+ SELECT gr.resource_id
+ FROM group_roles gr
+ WHERE
+ gr.role=#{role}
+ and (gr.group_id is null or gr.group_id in (select gu.group_id from groups_users gu where gu.user_id=#{userId}))
+ and
+ <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">gr.resource_id=#{element}</foreach>
+ UNION
+ SELECT p.id
+ FROM user_roles ur, projects p
+ WHERE
+ ur.role=#{role}
+ and ur.user_id=#{userId} and
+ <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">p.id=#{element}</foreach>
+ </select>
+
+ <select id="keepAuthorizedProjectIdsForAnonymous" parameterType="map" resultType="long">
+ SELECT gr.resource_id
+ FROM group_roles gr
+ WHERE
+ gr.role=#{role}
+ and gr.group_id is null
+ and
+ <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">gr.resource_id=#{element}</foreach>
+ </select>
+
<select id="selectAuthorizedRootProjectsKeys" parameterType="map" resultType="string">
<include refid="selectAuthorizedRootProjectsKeysQuery" />
</select>
package org.sonar.core.user;
import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.DbSession;
import java.util.Collection;
-import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
public class AuthorizationDaoTest extends AbstractDaoTestCase {
private static final int USER = 100;
- private static final String PROJECT = "pj-w-snapshot", PACKAGE = "pj-w-snapshot:package", FILE = "pj-w-snapshot:file", FILE_IN_OTHER_PROJECT = "another",
- EMPTY_PROJECT = "pj-wo-snapshot";
+ private static final Long PROJECT_ID = 300L, EMPTY_PROJECT_ID = 400L;
+ private static final String PROJECT = "pj-w-snapshot";
+
+ DbSession session;
+
+ @Before
+ public void setUp() throws Exception {
+ session = getMyBatis().openSession(false);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ session.close();
+ }
@Test
public void user_should_be_authorized() {
setupData("user_should_be_authorized");
AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
- Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
USER, "user");
- assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+ assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
// user does not have the role "admin"
- componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE),
+ componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID),
USER, "admin");
assertThat(componentIds).isEmpty();
}
setupData("group_should_be_authorized");
AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
- Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
USER, "user");
- assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+ assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
// group does not have the role "admin"
- componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
USER, "admin");
assertThat(componentIds).isEmpty();
}
setupData("group_should_have_global_authorization");
AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
- Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
USER, "user");
- assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+ assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
// group does not have the role "admin"
- componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
USER, "admin");
assertThat(componentIds).isEmpty();
}
setupData("anonymous_should_be_authorized");
AuthorizationDao authorization = new AuthorizationDao(getMyBatis());
- Set<String> componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT),
+ Collection<Long> componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID),
null, "user");
- assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT);
+ assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID);
// group does not have the role "admin"
- componentIds = authorization.keepAuthorizedComponentKeys(
- Sets.<String>newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT),
+ componentIds = authorization.keepAuthorizedProjectIds(session,
+ Sets.newHashSet(PROJECT_ID),
null, "admin");
assertThat(componentIds).isEmpty();
}
<groups_users user_id="100" group_id="200"/>
<group_roles id="1" group_id="200" resource_id="999" role="user"/>
- <projects id="301" kee="pj-w-snapshot:package" root_id="300" uuid="ABCD" module_uuid="DEFG"/>
- <projects id="302" kee="pj-w-snapshot:file" root_id="300" uuid="BCDE" module_uuid="DEFG"/>
- <projects id="303" kee="pj-w-snapshot:other" root_id="300" uuid="CDEF" module_uuid="DEFG"/>
<projects id="300" kee="pj-w-snapshot" uuid="DEFG" module_uuid="[null]"/>
<projects id="400" kee="pj-wo-snapshot" uuid="EFGH" module_uuid="[null]"/>
</dataset>