import org.sonar.server.exceptions.UnauthorizedException;
import static org.apache.commons.lang.StringUtils.defaultString;
+import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.server.user.UserSession.IdentityProvider.SONARQUBE;
public abstract class AbstractUserSession implements UserSession {
if (isRoot()) {
return true;
}
- return hasChildProjectsPermission(permission, component.uuid());
+ String applicationUuid = defaultString(component.getMainBranchProjectUuid(), component.projectUuid());
+ return hasChildProjectsPermission(permission, applicationUuid);
+ }
+
+ @Override
+ public final boolean hasChildProjectsPermission(String permission, ProjectDto project) {
+ if (isRoot()) {
+ return true;
+ }
+ return hasChildProjectsPermission(permission, project.getUuid());
}
@Override
@Override
public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
- if (isRoot() || !component.qualifier().equals(Qualifiers.APP) || hasChildProjectsPermission(projectPermission, component.uuid())) {
+ if (isRoot() || !APP.equals(component.qualifier()) || hasChildProjectsPermission(projectPermission, component)) {
return this;
}
throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
+ }
+
+ @Override
+ public UserSession checkChildProjectsPermission(String projectPermission, ProjectDto application) {
+ if (isRoot() || !APP.equals(application.getQualifier()) || hasChildProjectsPermission(projectPermission, application)) {
+ return this;
+ }
+ throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}
@Override
return this;
}
+ @Override
+ public UserSession checkChildProjectsPermission(String projectPermission, ProjectDto application) {
+ get().checkChildProjectsPermission(projectPermission, application);
+ return this;
+ }
+
@Override
public UserSession checkComponentUuidPermission(String permission, String componentUuid) {
get().checkComponentUuidPermission(permission, componentUuid);
return get().hasChildProjectsPermission(permission, component);
}
+ @Override
+ public boolean hasChildProjectsPermission(String permission, ProjectDto project) {
+ return get().hasChildProjectsPermission(permission, project);
+ }
+
@Override
public boolean hasComponentUuidPermission(String permission, String componentUuid) {
return get().hasComponentUuidPermission(permission, componentUuid);
boolean hasChildProjectsPermission(String permission, ComponentDto component);
+ boolean hasChildProjectsPermission(String permission, ProjectDto component);
+
/**
* Using {@link #hasComponentPermission(String, ComponentDto)} is recommended
* because it does not have to load project if the referenced component
*/
UserSession checkChildProjectsPermission(String projectPermission, ComponentDto project);
+ /**
+ * Ensures that {@link #hasChildProjectsPermission(String, ProjectDto)} is {@code true}
+ * otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.
+ */
+ UserSession checkChildProjectsPermission(String projectPermission, ProjectDto application);
+
/**
* Ensures that {@link #hasComponentUuidPermission(String, String)} is {@code true},
* otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.api.resources.Qualifiers;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.GroupTesting;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.tester.AnonymousMockUserSession;
import org.sonar.server.tester.MockUserSession;
assertThat(threadLocalUserSession.shouldResetPassword()).isTrue();
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getUuid).containsOnly(group.getUuid());
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ComponentDto())).isFalse();
+ assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ProjectDto())).isFalse();
}
@Test
.isInstanceOf(UnauthorizedException.class);
}
+ @Test
+ public void throw_ForbiddenException_when_no_access_to_applications_projects() {
+ GroupDto group = GroupTesting.newGroupDto();
+ MockUserSession expected = new MockUserSession("karadoc")
+ .setUuid("karadoc-uuid")
+ .setResetPassword(true)
+ .setLastSonarlintConnectionDate(1000L)
+ .setGroups(group);
+ threadLocalUserSession.set(expected);
+
+ ComponentDto componentDto = new ComponentDto().setQualifier(Qualifiers.APP).setMainBranchProjectUuid("component-uuid");
+ ProjectDto projectDto = new ProjectDto().setQualifier(Qualifiers.APP).setUuid("project-uuid");
+ assertThatThrownBy(() -> threadLocalUserSession.checkChildProjectsPermission(USER, componentDto))
+ .isInstanceOf(ForbiddenException.class);
+ assertThatThrownBy(() -> threadLocalUserSession.checkChildProjectsPermission(USER, projectDto))
+ .isInstanceOf(ForbiddenException.class);
+ }
+
+ @Test
+ public void checkChildProjectsPermission_gets_session_when_user_has_access_to_applications_projects() {
+ GroupDto group = GroupTesting.newGroupDto();
+ MockUserSession expected = new MockUserSession("karadoc")
+ .setUuid("karadoc-uuid")
+ .setResetPassword(true)
+ .setLastSonarlintConnectionDate(1000L)
+ .setGroups(group);
+
+ ProjectDto subProjectDto = new ProjectDto().setQualifier(Qualifiers.PROJECT).setUuid("subproject-uuid");
+ ComponentDto applicationAsComponentDto = new ComponentDto().setQualifier(Qualifiers.APP).setUuid("application-component-uuid").setProjectUuid("application-project-uuid");
+ ProjectDto applicationAsProjectDto = new ProjectDto().setQualifier(Qualifiers.APP).setUuid("application-project-uuid");
+
+ expected.registerProjects(subProjectDto);
+ expected.registerApplication(applicationAsProjectDto, subProjectDto);
+ expected.registerComponents(applicationAsComponentDto);
+ threadLocalUserSession.set(expected);
+
+ assertThat(threadLocalUserSession.checkChildProjectsPermission(USER, applicationAsComponentDto)).isEqualTo(threadLocalUserSession);
+ assertThat(threadLocalUserSession.checkChildProjectsPermission(USER, applicationAsProjectDto)).isEqualTo(threadLocalUserSession);
+ }
+
}
return currentUserSession.hasChildProjectsPermission(permission, component);
}
+ @Override
+ public boolean hasChildProjectsPermission(String permission, ProjectDto component) {
+ return currentUserSession.hasChildProjectsPermission(permission, component);
+ }
+
@Override
public boolean hasComponentUuidPermission(String permission, String componentUuid) {
return currentUserSession.hasComponentUuidPermission(permission, componentUuid);
return this;
}
+ @Override
+ public UserSession checkChildProjectsPermission(String projectPermission, ProjectDto application) {
+ currentUserSession.checkChildProjectsPermission(projectPermission, application);
+ return this;
+ }
+
@Override
public UserSession checkComponentUuidPermission(String permission, String componentUuid) {
currentUserSession.checkComponentUuidPermission(permission, componentUuid);
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.resources.Scopes;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
-import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
/**
ComponentDto baseComponent = loadComponent(dbSession, wsRequest);
checkPermissions(baseComponent);
Optional<SnapshotDto> baseSnapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, baseComponent.projectUuid());
- if (!baseSnapshot.isPresent()) {
+ if (baseSnapshot.isEmpty()) {
return ComponentTreeData.builder()
.setBaseComponent(baseComponent)
.build();
private void checkPermissions(ComponentDto baseComponent) {
userSession.checkComponentPermission(UserRole.USER, baseComponent);
+
+ if (Scopes.PROJECT.equals(baseComponent.scope()) && Qualifiers.APP.equals(baseComponent.qualifier())) {
+ userSession.checkChildProjectsPermission(UserRole.USER, baseComponent);
+ }
}
public static boolean isFileComponent(@Nonnull ComponentDto input) {
import java.util.stream.Stream;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
private ComponentDto searchComponent(SearchHistoryRequest request, DbSession dbSession) {
ComponentDto component = loadComponent(dbSession, request);
userSession.checkComponentPermission(UserRole.USER, component);
+ if (Scopes.PROJECT.equals(component.scope()) && Qualifiers.APP.equals(component.qualifier())) {
+ userSession.checkChildProjectsPermission(UserRole.USER, component);
+ }
return component;
}
private void checkPermission(ComponentDto project) {
userSession.checkComponentPermission(UserRole.USER, project);
+ if (Scopes.PROJECT.equals(project.scope()) && Qualifiers.APP.equals(project.qualifier())) {
+ userSession.checkChildProjectsPermission(UserRole.USER, project);
+ }
}
private void addProject(SearchData.Builder data) {
import static org.sonar.api.server.ws.WebService.Param.SORT;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.db.component.ComponentDbTester.toProjectDto;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
ComponentDto project = db.components().insertPrivateProject();
db.components().insertSnapshot(project);
- assertThatThrownBy(() -> {
- ws.newRequest()
- .setParam(PARAM_COMPONENT, project.getKey())
- .setParam(PARAM_METRIC_KEYS, "ncloc")
- .executeProtobuf(ComponentTreeWsResponse.class);
- })
+ var request = ws.newRequest()
+ .setParam(PARAM_COMPONENT, project.getKey())
+ .setParam(PARAM_METRIC_KEYS, "ncloc");
+ assertThatThrownBy(() -> request.executeProtobuf(ComponentTreeWsResponse.class))
+ .isInstanceOf(ForbiddenException.class);
+ }
+
+ @Test
+ public void fail_when_app_with_insufficient_privileges_for_projects() {
+ userSession.logIn();
+ ComponentDto app = db.components().insertPrivateApplication();
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+ db.components().insertSnapshot(app);
+
+ userSession.registerApplication(
+ toProjectDto(app, 1L),
+ toProjectDto(project1, 1L),
+ toProjectDto(project2, 1L));
+
+ userSession.addProjectPermission(UserRole.USER, app, project1);
+
+ var request = ws.newRequest()
+ .setParam(PARAM_COMPONENT, app.getKey())
+ .setParam(PARAM_METRIC_KEYS, "ncloc");
+ assertThatThrownBy(() -> request.executeProtobuf(ComponentTreeWsResponse.class))
.isInstanceOf(ForbiddenException.class);
}
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.db.component.ComponentDbTester.toProjectDto;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED;
.setParam(PARAM_COMPONENT, branch.getDbKey())
.setParam(PARAM_METRICS, "ncloc")
.execute())
- .isInstanceOf(NotFoundException.class)
- .hasMessageContaining(format("Component key '%s' not found", branch.getDbKey()));
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining(format("Component key '%s' not found", branch.getDbKey()));
}
@Test
.isInstanceOf(ForbiddenException.class);
}
+ @Test
+ public void fail_if_not_enough_permissions_for_application() {
+ ComponentDto application = db.components().insertPrivateApplication();
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ userSession.logIn()
+ .registerApplication(
+ toProjectDto(application, 1L),
+ toProjectDto(project1, 1L),
+ toProjectDto(project2, 1L))
+ .addProjectPermission(UserRole.USER, application, project1);
+
+ SearchHistoryRequest request = SearchHistoryRequest.builder()
+ .setComponent(application.getDbKey())
+ .setMetrics(singletonList(complexityMetric.getKey()))
+ .build();
+
+ assertThatThrownBy(() -> call(request))
+ .isInstanceOf(ForbiddenException.class);
+ }
+
@Test
public void fail_if_unknown_component() {
SearchHistoryRequest request = SearchHistoryRequest.builder()
.setParam(PARAM_COMPONENT, "file-key")
.setParam(PARAM_METRICS, "ncloc")
.execute())
- .isInstanceOf(NotFoundException.class)
- .hasMessageContaining("Component key 'file-key' not found");
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining("Component key 'file-key' not found");
}
@Test
.setParam(PARAM_BRANCH, "another_branch")
.setParam(PARAM_METRICS, "ncloc")
.execute())
- .isInstanceOf(NotFoundException.class)
- .hasMessageContaining(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch"));
+ .isInstanceOf(NotFoundException.class)
+ .hasMessageContaining(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch"));
}
@Test
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.db.component.BranchType.BRANCH;
+import static org.sonar.db.component.ComponentDbTester.toProjectDto;
import static org.sonar.db.component.ComponentTesting.newBranchDto;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
db.events().insertEvent(newEvent(a1).setUuid("AXt91FkXy_c4CIP4ds6A")
.setName("Failed")
.setCategory(QUALITY_GATE.getLabel())
- .setDescription("Coverage on New Code < 85, Reliability Rating > 4, Maintainability Rating on New Code > 1, Reliability Rating on New Code > 1, Security Rating on New Code > 1, Duplicated Lines (%) on New Code > 3"));
+ .setDescription(
+ "Coverage on New Code < 85, Reliability Rating > 4, Maintainability Rating on New Code > 1, Reliability Rating on New Code > 1, Security Rating on New Code > 1, Duplicated Lines (%) on New Code > 3"));
db.events().insertEvent(newEvent(a1).setUuid("AXx_QFJ6Wa8wkfuJ6r5P")
.setName("6.3").setCategory(VERSION.getLabel()));
db.events().insertEvent(newEvent(a2).setUuid("E21")
@Test
public void return_analyses_of_application() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
SnapshotDto secondAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(2_000_000L));
SnapshotDto thirdAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(3_000_000L));
@Test
public void return_definition_change_events_on_application_analyses() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
@UseDataProvider("changedBranches")
public void application_definition_change_with_branch(@Nullable String oldBranch, @Nullable String newBranch) {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
EventComponentChangeDto changeDto1 = generateEventComponentChange(event, REMOVED, "My project", "app1", oldBranch, uuidFactoryFast.create());
@Test
public void incorrect_eventcomponentchange_two_identical_changes_added_on_same_project() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
@Test
public void incorrect_eventcomponentchange_incorrect_category() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
EventComponentChangeDto changeDto1 = generateEventComponentChange(event, FAILED_QUALITY_GATE, "My project", "app1", "master", uuidFactoryFast.create());
@Test
public void incorrect_eventcomponentchange_three_component_changes_on_same_project() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(newEvent(firstAnalysis).setName("").setUuid("E11").setCategory(DEFINITION_CHANGE.getLabel()));
EventComponentChangeDto changeDto1 = generateEventComponentChange(event, ADDED, "My project", "app1", "master", uuidFactoryFast.create());
@Test
public void incorrect_quality_gate_information() {
ComponentDto application = db.components().insertPublicApplication();
- userSession.registerComponents(application);
+ userSession.registerApplication(toProjectDto(application, 1L));
SnapshotDto firstAnalysis = db.components().insertSnapshot(newAnalysis(application).setCreatedAt(1_000_000L));
EventDto event = db.events().insertEvent(
newEvent(firstAnalysis)
.isInstanceOf(ForbiddenException.class);
}
+ @Test
+ public void fail_if_not_enough_permissions_on_applications_projects() {
+ ComponentDto application = db.components().insertPrivateApplication();
+ ComponentDto project1 = db.components().insertPrivateProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+
+ userSession.logIn()
+ .registerApplication(
+ toProjectDto(application, 1L),
+ toProjectDto(project1, 1L),
+ toProjectDto(project2, 1L))
+ .addProjectPermission(UserRole.USER, application, project1);
+
+ var projectDbKey = application.getDbKey();
+ assertThatThrownBy(() -> call(projectDbKey))
+ .isInstanceOf(ForbiddenException.class);
+ }
+
@Test
public void fail_if_project_does_not_exist() {
assertThatThrownBy(() -> call("P1"))