diff --git a/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java b/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java index 1d3126a4232..4dee6ad79fe 100644 --- a/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java +++ b/server/sonar-ce/src/main/java/org/sonar/ce/user/CeUserSession.java @@ -23,8 +23,8 @@ import java.util.List; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.db.user.GroupDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.user.GroupDto; import org.sonar.server.user.UserSession; /** @@ -42,7 +42,8 @@ public String getLogin() { throw notImplemented(); } - @Override public String getUuid() { + @Override + public String getUuid() { throw notImplemented(); } @@ -136,6 +137,16 @@ public List keepAuthorizedComponents(String permission, Collection throw notImplemented(); } + @Override + public boolean hasMembership(OrganizationDto organization) { + throw notImplemented(); + } + + @Override + public UserSession checkMembership(OrganizationDto organization) { + throw notImplemented(); + } + private static RuntimeException notImplemented() { throw new UnsupportedOperationException(UOE_MESSAGE); } diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/user/BaseUserSession.java b/server/sonar-server-common/src/main/java/org/sonar/server/user/BaseUserSession.java index 913f285ded4..51c591cd62f 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/user/BaseUserSession.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/user/BaseUserSession.java @@ -68,6 +68,13 @@ public final boolean hasComponentUuidPermission(String permission, String compon protected abstract boolean hasProjectUuidPermission(String permission, String projectUuid); + @Override + public final boolean hasMembership(OrganizationDto organization) { + return isRoot() || hasMembershipImpl(organization); + } + + protected abstract boolean hasMembershipImpl(OrganizationDto organization); + @Override public final List keepAuthorizedComponents(String permission, Collection components) { if (isRoot()) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server-common/src/main/java/org/sonar/server/user/UserSession.java index 4eca7b6b46c..645254afcf3 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/user/UserSession.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/user/UserSession.java @@ -173,4 +173,20 @@ public interface UserSession { */ UserSession checkIsSystemAdministrator(); + /** + * Returns {@code true} if the user is member of the organization, otherwise {@code false}. + * + * If the organization does not exist, then returns {@code false}. + * + * Always returns {@code true} if {@link #isRoot()} is {@code true}, even if + * organization does not exist. + */ + boolean hasMembership(OrganizationDto organization); + + /** + * Ensures that {@link #hasMembership(OrganizationDto)} is {@code true}, + * otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}. + */ + UserSession checkMembership(OrganizationDto organization); + } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/user/LightUserSessionRule.java b/server/sonar-server-common/src/test/java/org/sonar/server/user/LightUserSessionRule.java index 0c39a08164e..640dcb9aa91 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/user/LightUserSessionRule.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/user/LightUserSessionRule.java @@ -145,6 +145,11 @@ protected boolean hasPermissionImpl(OrganizationPermission permission, String or throw new UnsupportedOperationException("hasPermissionImpl not implemented"); } + @Override + protected boolean hasMembershipImpl(OrganizationDto organization) { + throw new UnsupportedOperationException("hasMembershipImpl not implemented"); + } + @Override protected Optional componentUuidToProjectUuid(String componentUuid) { return Optional.ofNullable(projectUuidByComponentUuid.get(componentUuid)); @@ -233,4 +238,9 @@ public boolean isSystemAdministrator() { public UserSession checkIsSystemAdministrator() { throw new UnsupportedOperationException("checkIsSystemAdministrator not implemented"); } + + @Override + public UserSession checkMembership(OrganizationDto organization) { + throw new UnsupportedOperationException("checkMembership not implemented"); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java index 177ce73ceb1..91330f3917e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java @@ -24,6 +24,7 @@ import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.concurrent.Immutable; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.user.GroupDto; import org.sonar.server.user.AbstractUserSession; @@ -89,4 +90,9 @@ public boolean isRoot() { public boolean isSystemAdministrator() { return false; } + + @Override + public boolean hasMembershipImpl(OrganizationDto organization) { + return false; + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java index 58685554fbc..d50a8ca0c82 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java @@ -39,6 +39,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ORGANIZATION; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; @@ -100,7 +101,9 @@ OrganizationDto getOrganization(DbSession dbSession, Request request) { String organizationKey = Optional.ofNullable(request.param(PARAM_ORGANIZATION)) .orElseGet(() -> defaultOrganizationProvider.get().getKey()); Optional organizationDto = dbClient.organizationDao().selectByKey(dbSession, organizationKey); - return checkFoundWithOptional(organizationDto, "No organization with key '%s'", organizationKey); + OrganizationDto organization = checkFoundWithOptional(organizationDto, "No organization with key '%s'", organizationKey); + checkMembershipOnPaidOrganization(organization); + return organization; } void checkCanEdit(QGateWithOrgDto qualityGate) { @@ -127,4 +130,11 @@ private static void checkNotBuiltIn(QualityGateDto qualityGate) { checkArgument(!qualityGate.isBuiltIn(), "Operation forbidden for built-in Quality Gate '%s'", qualityGate.getName()); } + private void checkMembershipOnPaidOrganization(OrganizationDto organization) { + if (!organization.getSubscription().equals(PAID)) { + return; + } + userSession.checkMembership(organization); + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java index 489f08f52e0..cc7a09fc15f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java @@ -33,7 +33,6 @@ import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; -import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; @@ -74,7 +73,7 @@ public OrganizationDto getOrganization(DbSession dbSession, QProfileDto profile) String organizationUuid = profile.getOrganizationUuid(); OrganizationDto organization = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid) .orElseThrow(() -> new IllegalStateException("Cannot load organization with uuid=" + organizationUuid)); - checkMembershipOnPaidOrganization(dbSession, organization); + checkMembershipOnPaidOrganization(organization); return organization; } @@ -84,7 +83,7 @@ public OrganizationDto getOrganizationByKey(DbSession dbSession, @Nullable Strin OrganizationDto organization = checkFoundWithOptional( dbClient.organizationDao().selectByKey(dbSession, organizationOrDefaultKey), "No organization with key '%s'", organizationOrDefaultKey); - checkMembershipOnPaidOrganization(dbSession, organization); + checkMembershipOnPaidOrganization(organization); return organization; } @@ -174,15 +173,11 @@ private boolean isMember(DbSession dbSession, OrganizationDto organization, int return dbClient.organizationMemberDao().select(dbSession, organization.getUuid(), userId).isPresent(); } - private void checkMembershipOnPaidOrganization(DbSession dbSession, OrganizationDto organization) { + private void checkMembershipOnPaidOrganization(OrganizationDto organization) { if (!organization.getSubscription().equals(PAID)) { return; } - Integer userId = userSession.getUserId(); - if (userId != null && isMember(dbSession, organization, userId)) { - return; - } - throw new ForbiddenException(String.format("You're not member of organization '%s'", organization.getKey())); + userSession.checkMembership(organization); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java index 6755309c9bf..0df84c49176 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleQueryFactory.java @@ -34,16 +34,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; -import static org.sonar.server.util.EnumUtils.toEnums; -import static org.sonar.server.ws.WsUtils.checkFound; -import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; +import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INCLUDE_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE; -import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INCLUDE_EXTERNAL; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE; @@ -54,6 +51,9 @@ import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TAGS; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TEMPLATE_KEY; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_TYPES; +import static org.sonar.server.util.EnumUtils.toEnums; +import static org.sonar.server.ws.WsUtils.checkFound; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; @ServerSide public class RuleQueryFactory { @@ -139,6 +139,7 @@ private void setOrganization(DbSession dbSession, RuleQuery query, Request reque checkArgument(organization.getUuid().equals(inputOrganization.getUuid()), format("The specified quality profile '%s' is not part of the specified organization '%s'", profile.getKee(), organizationKey)); } + wsSupport.checkMembershipOnPaidOrganization(organization); query.setOrganization(organization); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java index ecd4b1bf592..a54dea98130 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/ws/RuleWsSupport.java @@ -33,11 +33,12 @@ import org.sonar.db.user.UserDto; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; -import org.sonar.server.ws.WsUtils; import static org.sonar.core.util.stream.MoreCollectors.toSet; import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; @ServerSide public class RuleWsSupport { @@ -61,9 +62,11 @@ public void checkQProfileAdminPermissionOnDefaultOrganization() { public OrganizationDto getOrganizationByKey(DbSession dbSession, @Nullable String organizationKey) { String organizationOrDefaultKey = Optional.ofNullable(organizationKey) .orElseGet(defaultOrganizationProvider.get()::getKey); - return WsUtils.checkFoundWithOptional( + OrganizationDto organization = checkFoundWithOptional( dbClient.organizationDao().selectByKey(dbSession, organizationOrDefaultKey), "No organization with key '%s'", organizationOrDefaultKey); + checkMembershipOnPaidOrganization(organization); + return organization; } Map getUsersByUuid(DbSession dbSession, List rules) { @@ -71,4 +74,11 @@ Map getUsersByUuid(DbSession dbSession, List rules) { return dbClient.userDao().selectByUuids(dbSession, userUuids).stream().collect(uniqueIndex(UserDto::getUuid)); } + void checkMembershipOnPaidOrganization(OrganizationDto organization) { + if (!organization.getSubscription().equals(PAID)) { + return; + } + userSession.checkMembership(organization); + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java index 2a24d1d19cd..ee54c942351 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/AbstractUserSession.java @@ -25,6 +25,8 @@ import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; +import static java.lang.String.format; + public abstract class AbstractUserSession extends BaseUserSession { private static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges"; private static final ForbiddenException INSUFFICIENT_PRIVILEGES_EXCEPTION = new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE); @@ -86,4 +88,13 @@ public final UserSession checkIsSystemAdministrator() { } return this; } + + @Override + public UserSession checkMembership(OrganizationDto organization) { + if (!hasMembership(organization)) { + throw new ForbiddenException(format("You're not member of organization '%s'", organization.getKey())); + } + return this; + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java b/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java index a016a7347eb..4f1ccc5b97a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/DoPrivileged.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Optional; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.user.GroupDto; @@ -116,11 +117,16 @@ protected Optional componentUuidToProjectUuid(String componentUuid) { protected boolean hasProjectUuidPermission(String permission, String projectUuid) { return true; } - + @Override public boolean isSystemAdministrator() { return true; } + + @Override + public boolean hasMembershipImpl(OrganizationDto organization) { + return true; + } } private void start() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java index c148cd5945f..504b2929032 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ServerUserSession.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -36,13 +37,15 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.organization.OrganizationMemberDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.OrganizationFlags; -import static com.google.common.collect.Maps.newHashMap; +import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.defaultIfEmpty; /** @@ -56,9 +59,10 @@ public class ServerUserSession extends AbstractUserSession { private final DefaultOrganizationProvider defaultOrganizationProvider; private final Supplier> groups = Suppliers.memoize(this::loadGroups); private final Supplier isSystemAdministratorSupplier = Suppliers.memoize(this::loadIsSystemAdministrator); - private final Map projectUuidByComponentUuid = newHashMap(); + private final Map projectUuidByComponentUuid = new HashMap<>(); private Map> permissionsByOrganizationUuid; private Map> permissionsByProjectUuid; + private Set organizationMembership = new HashSet<>(); ServerUserSession(DbClient dbClient, OrganizationFlags organizationFlags, DefaultOrganizationProvider defaultOrganizationProvider, @Nullable UserDto userDto) { @@ -222,4 +226,29 @@ private boolean loadIsSystemAdministrator() { return false; } } + + @Override + public boolean hasMembershipImpl(OrganizationDto organization) { + return isMember(organization); + } + + private boolean isMember(OrganizationDto organization) { + if (!isLoggedIn()) { + return false; + } + if (isRoot()) { + return true; + } + String organizationUuid = organization.getUuid(); + if (organizationMembership.contains(organizationUuid)) { + return true; + } + try (DbSession dbSession = dbClient.openSession(false)) { + Optional organizationMemberDto = dbClient.organizationMemberDao().select(dbSession, organizationUuid, requireNonNull(getUserId())); + if (organizationMemberDto.isPresent()) { + organizationMembership.add(organizationUuid); + } + return organizationMembership.contains(organizationUuid); + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java index 0d522a628ad..79287e99fad 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java @@ -24,9 +24,9 @@ import javax.annotation.CheckForNull; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.user.GroupDto; import org.sonar.server.exceptions.UnauthorizedException; -import org.sonar.db.permission.OrganizationPermission; /** * Part of the current HTTP session @@ -164,4 +164,15 @@ public boolean hasPermission(OrganizationPermission permission, OrganizationDto public List keepAuthorizedComponents(String permission, Collection components) { return get().keepAuthorizedComponents(permission, components); } + + @Override + public boolean hasMembership(OrganizationDto organization) { + return get().hasMembership(organization); + } + + @Override + public UserSession checkMembership(OrganizationDto organization) { + get().checkMembership(organization); + return this; + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java index 89dc22cfbeb..27cd019b55b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java @@ -24,6 +24,7 @@ import org.sonar.db.permission.OrganizationPermission; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class SafeModeUserSessionTest { @@ -45,5 +46,6 @@ public void session_has_no_permissions() { assertThat(underTest.isSystemAdministrator()).isFalse(); assertThat(underTest.hasPermissionImpl(OrganizationPermission.ADMINISTER, "foo")).isFalse(); assertThat(underTest.hasProjectUuidPermission(UserRole.USER, "foo")).isFalse(); + assertThat(underTest.hasMembership(newOrganizationDto())).isFalse(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java index 8f8b3abf63e..8613419f018 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java @@ -29,6 +29,8 @@ import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.qualitygate.QualityGateFinder; @@ -36,8 +38,10 @@ import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.Qualitygates.ListWsResponse.QualityGate; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; import static org.sonar.test.JsonAssert.assertJson; @@ -59,40 +63,6 @@ public class ListActionTest { private WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient(), new QualityGatesWsSupport(dbClient, userSession, defaultOrganizationProvider), qualityGateFinder)); - @Test - public void verify_definition() { - WebService.Action action = ws.getDef(); - assertThat(action.since()).isEqualTo("4.3"); - assertThat(action.key()).isEqualTo("list"); - assertThat(action.isPost()).isFalse(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription) - .containsExactlyInAnyOrder( - tuple("7.0", "'isDefault' field is added on quality gate"), - tuple("7.0", "'default' field on root level is deprecated"), - tuple("7.0", "'isBuiltIn' field is added in the response"), - tuple("7.0", "'actions' fields are added in the response")); - assertThat(action.params()).extracting(WebService.Param::key, WebService.Param::isRequired) - .containsExactlyInAnyOrder(tuple("organization", false)); - } - - @Test - public void json_example() { - OrganizationDto organization = db.organizations().insert(); - userSession.logIn("admin").addPermission(ADMINISTER_QUALITY_GATES, organization); - QualityGateDto defaultQualityGate = db.qualityGates().insertQualityGate(organization, qualityGate -> qualityGate.setName("Sonar way").setBuiltIn(true)); - db.qualityGates().insertQualityGate(organization, qualityGate -> qualityGate.setName("Sonar way - Without Coverage").setBuiltIn(false)); - db.qualityGates().setDefaultQualityGate(organization, defaultQualityGate); - - String response = ws.newRequest() - .setParam("organization", organization.getKey()) - .execute() - .getInput(); - - assertJson(response).ignoreFields("id", "default") - .isSimilarTo(getClass().getResource("list-example.json")); - } - @Test public void list_quality_gates() { OrganizationDto organization = db.organizations().insert(); @@ -220,4 +190,69 @@ public void actions_without_quality_gate_administer_permission() { tuple(defaultQualityGate.getName(), false, false, false, false, false, false), tuple(otherQualityGate.getName(), false, false, false, false, false, false)); } + + @Test + public void list_quality_gates_on_paid_organization() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); + db.qualityGates().setDefaultQualityGate(organization, qualityGate); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); + + ListWsResponse response = ws.newRequest() + .setParam("organization", organization.getKey()) + .executeProtobuf(ListWsResponse.class); + + assertThat(response.getQualitygatesList()) + .extracting(QualityGate::getName) + .containsExactlyInAnyOrder(qualityGate.getName()); + } + + @Test + public void fail_on_paid_organization_when_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam("organization", organization.getKey()) + .execute(); + } + + @Test + public void json_example() { + OrganizationDto organization = db.organizations().insert(); + userSession.logIn("admin").addPermission(ADMINISTER_QUALITY_GATES, organization); + QualityGateDto defaultQualityGate = db.qualityGates().insertQualityGate(organization, qualityGate -> qualityGate.setName("Sonar way").setBuiltIn(true)); + db.qualityGates().insertQualityGate(organization, qualityGate -> qualityGate.setName("Sonar way - Without Coverage").setBuiltIn(false)); + db.qualityGates().setDefaultQualityGate(organization, defaultQualityGate); + + String response = ws.newRequest() + .setParam("organization", organization.getKey()) + .execute() + .getInput(); + + assertJson(response).ignoreFields("id", "default") + .isSimilarTo(getClass().getResource("list-example.json")); + } + + @Test + public void verify_definition() { + WebService.Action action = ws.getDef(); + assertThat(action.since()).isEqualTo("4.3"); + assertThat(action.key()).isEqualTo("list"); + assertThat(action.isPost()).isFalse(); + assertThat(action.isInternal()).isFalse(); + assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription) + .containsExactlyInAnyOrder( + tuple("7.0", "'isDefault' field is added on quality gate"), + tuple("7.0", "'default' field on root level is deprecated"), + tuple("7.0", "'isBuiltIn' field is added in the response"), + tuple("7.0", "'actions' fields are added in the response")); + assertThat(action.params()).extracting(WebService.Param::key, WebService.Param::isRequired) + .containsExactlyInAnyOrder(tuple("organization", false)); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ShowActionTest.java index f12a1abf518..debea374c14 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ShowActionTest.java @@ -32,6 +32,8 @@ import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; @@ -44,6 +46,7 @@ import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; import static org.sonar.test.JsonAssert.assertJson; @@ -64,44 +67,6 @@ public class ShowActionTest { new ShowAction(db.getDbClient(), new QualityGateFinder(db.getDbClient()), new QualityGatesWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider))); - @Test - public void verify_definition() { - WebService.Action action = ws.getDef(); - assertThat(action.since()).isEqualTo("4.3"); - assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription) - .containsExactlyInAnyOrder( - tuple("7.0", "'isBuiltIn' field is added to the response"), - tuple("7.0", "'actions' field is added in the response")); - assertThat(action.params()) - .extracting(Param::key, Param::isRequired) - .containsExactlyInAnyOrder( - tuple("id", false), - tuple("name", false), - tuple("organization", false)); - } - - @Test - public void json_example() { - OrganizationDto organization = db.organizations().insert(); - userSession.logIn("admin").addPermission(ADMINISTER_QUALITY_GATES, organization); - QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization, qg -> qg.setName("My Quality Gate")); - QGateWithOrgDto qualityGate2 = db.qualityGates().insertQualityGate(organization, qg -> qg.setName("My Quality Gate 2")); - db.qualityGates().setDefaultQualityGate(organization, qualityGate2); - MetricDto blockerViolationsMetric = db.measures().insertMetric(m -> m.setKey("blocker_violations")); - MetricDto criticalViolationsMetric = db.measures().insertMetric(m -> m.setKey("critical_violations")); - db.qualityGates().addCondition(qualityGate, blockerViolationsMetric, c -> c.setOperator("GT").setPeriod(null).setErrorThreshold("0").setWarningThreshold(null)); - db.qualityGates().addCondition(qualityGate, criticalViolationsMetric, c -> c.setOperator("LT").setPeriod(1).setErrorThreshold(null).setWarningThreshold("0")); - - String response = ws.newRequest() - .setParam("name", qualityGate.getName()) - .setParam("organization", organization.getKey()) - .execute() - .getInput(); - - assertJson(response).ignoreFields("id") - .isSimilarTo(getClass().getResource("show-example.json")); - } - @Test public void show() { OrganizationDto organization = db.organizations().insert(); @@ -272,6 +237,24 @@ public void actions_when_not_quality_gate_administer() { assertThat(actions.getAssociateProjects()).isFalse(); } + @Test + public void show_on_paid_organization() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + db.qualityGates().setDefaultQualityGate(organization, qualityGate); + MetricDto metric = db.measures().insertMetric(); + db.qualityGates().addCondition(qualityGate, metric); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); + + ShowWsResponse response = ws.newRequest() + .setParam("name", qualityGate.getName()) + .setParam("organization", organization.getKey()) + .executeProtobuf(ShowWsResponse.class); + + assertThat(response.getConditionsList()).hasSize(1); + } + @Test public void fail_when_no_name_or_id() { OrganizationDto organization = db.organizations().insert(); @@ -390,4 +373,57 @@ public void fail_when_quality_gate_belongs_to_another_organization_using_id_para .setParam("organization", organization.getKey()) .execute(); } + + @Test + public void fail_on_paid_organization_when_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam("name", qualityGate.getName()) + .setParam("organization", organization.getKey()) + .execute(); + } + + @Test + public void json_example() { + OrganizationDto organization = db.organizations().insert(); + userSession.logIn("admin").addPermission(ADMINISTER_QUALITY_GATES, organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization, qg -> qg.setName("My Quality Gate")); + QGateWithOrgDto qualityGate2 = db.qualityGates().insertQualityGate(organization, qg -> qg.setName("My Quality Gate 2")); + db.qualityGates().setDefaultQualityGate(organization, qualityGate2); + MetricDto blockerViolationsMetric = db.measures().insertMetric(m -> m.setKey("blocker_violations")); + MetricDto criticalViolationsMetric = db.measures().insertMetric(m -> m.setKey("critical_violations")); + db.qualityGates().addCondition(qualityGate, blockerViolationsMetric, c -> c.setOperator("GT").setPeriod(null).setErrorThreshold("0").setWarningThreshold(null)); + db.qualityGates().addCondition(qualityGate, criticalViolationsMetric, c -> c.setOperator("LT").setPeriod(1).setErrorThreshold(null).setWarningThreshold("0")); + + String response = ws.newRequest() + .setParam("name", qualityGate.getName()) + .setParam("organization", organization.getKey()) + .execute() + .getInput(); + + assertJson(response).ignoreFields("id") + .isSimilarTo(getClass().getResource("show-example.json")); + } + + @Test + public void verify_definition() { + WebService.Action action = ws.getDef(); + assertThat(action.since()).isEqualTo("4.3"); + assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription) + .containsExactlyInAnyOrder( + tuple("7.0", "'isBuiltIn' field is added to the response"), + tuple("7.0", "'actions' field is added in the response")); + assertThat(action.params()) + .extracting(Param::key, Param::isRequired) + .containsExactlyInAnyOrder( + tuple("id", false), + tuple("name", false), + tuple("organization", false)); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java index 672d218c9fe..0524ea98fbe 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/BackupActionTest.java @@ -116,8 +116,7 @@ public void returns_backup_of_profile_on_paid_organization() { OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(A_LANGUAGE)); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); TestResponse response = tester.newRequest() .setParam("organization", organization.getKey()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java index 12498e06f90..3dee5f66033 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogActionTest.java @@ -368,8 +368,7 @@ public void changelog_on_no_more_existing_user() { public void changelog_on_paid_organization() { OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); QProfileDto qualityProfile = db.qualityProfiles().insert(organization); RuleDefinitionDto rule = db.rules().insert(); insertChange(qualityProfile, ActiveRuleChange.Type.ACTIVATED, db.users().insertUser(), diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionTest.java index 250b6c86686..52585ca17b4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CompareActionTest.java @@ -154,8 +154,7 @@ public void do_not_fail_to_compare_on_paid_organization() { QProfileDto left = db.qualityProfiles().insert(organization); QProfileDto right = db.qualityProfiles().insert(organization); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); ws.newRequest() .setParam("leftKey", left.getKee()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java index 85ae3a93525..e91f383e94a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ExportActionTest.java @@ -185,8 +185,7 @@ public void export_profile_in_paid_organization() { OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); QProfileDto profile = createProfile(organization, false); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); WsActionTester tester = newWsActionTester(newExporter("polop")); String result = tester.newRequest() diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java index 25b501a72bd..01d375228fe 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java @@ -217,8 +217,7 @@ public void inheritance_on_paid_organization() { OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); QProfileDto qualityProfile = db.qualityProfiles().insert(organization); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); ws.newRequest() .setParam(PARAM_ORGANIZATION, organization.getKey()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java index 506f769c7ee..e1f84a71093 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/SearchActionTest.java @@ -424,8 +424,7 @@ public void return_qprofile_on_paid_organization_when_user_is_member() { OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); QProfileDto qProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO1.getKey())); UserDto user = db.users().insertUser(); - db.organizations().addMember(organization, user); - userSession.logIn(user); + userSession.logIn(user).addMembership(organization); SearchWsResponse result = call(ws.newRequest().setParam(PARAM_ORGANIZATION, organization.getKey())); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/AppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/AppActionTest.java index 0d219a03efb..d2e73661f75 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/AppActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/AppActionTest.java @@ -30,15 +30,20 @@ import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.rule.RuleRepositoryDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.language.LanguageTesting; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.WsActionTester; +import org.sonarqube.ws.Rules; +import static java.lang.String.format; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.test.JsonAssert.assertJson; public class AppActionTest { @@ -57,11 +62,11 @@ public class AppActionTest { private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private RuleWsSupport wsSupport = new RuleWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider); private AppAction underTest = new AppAction(languages, db.getDbClient(), userSession, wsSupport); - private WsActionTester tester = new WsActionTester(underTest); + private WsActionTester ws = new WsActionTester(underTest); @Test public void test_definition() { - WebService.Action definition = tester.getDef(); + WebService.Action definition = ws.getDef(); assertThat(definition.isInternal()).isTrue(); assertThat(definition.key()).isEqualTo("app"); @@ -76,7 +81,7 @@ public void test_definition() { public void response_contains_rule_repositories() { insertRules(); - String json = tester.newRequest().execute().getInput(); + String json = ws.newRequest().execute().getInput(); assertJson(json).isSimilarTo("{" + "\"repositories\": [" + " {" + @@ -95,7 +100,7 @@ public void response_contains_rule_repositories() { @Test public void response_contains_languages() { - String json = tester.newRequest().execute().getInput(); + String json = ws.newRequest().execute().getInput(); assertJson(json).isSimilarTo("{" + "\"languages\": {" + @@ -106,20 +111,28 @@ public void response_contains_languages() { } @Test - public void throw_NotFoundException_if_organization_does_not_exist() { - expectedException.expect(NotFoundException.class); - expectedException.expectMessage("No organization with key 'does_not_exist'"); + public void response_on_paid_organization() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); - tester.newRequest() - .setParam("organization", "does_not_exist") - .execute(); + String json = ws.newRequest() + .setParam("organization", organization.getKey()) + .execute().getInput();; + + assertJson(json).isSimilarTo("{" + + "\"languages\": {" + + " \"xoo\": \"Xoo\"," + + " \"ws\": \"Whitespace\"" + + " }" + + "}"); } @Test public void canWrite_is_true_if_user_is_profile_administrator_of_default_organization() { userSession.addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, db.getDefaultOrganization()); - String json = tester.newRequest().execute().getInput(); + String json = ws.newRequest().execute().getInput(); assertJson(json).isSimilarTo("{ \"canWrite\": true }"); } @@ -129,7 +142,7 @@ public void canWrite_is_true_if_user_is_profile_administrator_of_specified_organ OrganizationDto organization = db.organizations().insert(); userSession.addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); - String json = tester.newRequest() + String json = ws.newRequest() .setParam("organization", organization.getKey()) .execute().getInput(); @@ -142,7 +155,7 @@ public void canWrite_is_false_if_user_is_not_profile_administrator_of_specified_ OrganizationDto organization2 = db.organizations().insert(); userSession.addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization1); - String json = tester.newRequest() + String json = ws.newRequest() .setParam("organization", organization2.getKey()) .execute().getInput(); @@ -154,11 +167,34 @@ public void canWrite_is_false_if_user_is_not_profile_administrator_of_default_or OrganizationDto organization = db.organizations().insert(); userSession.addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); - String json = tester.newRequest().execute().getInput(); + String json = ws.newRequest().execute().getInput(); assertJson(json).isSimilarTo("{ \"canWrite\": false }"); } + @Test + public void throw_NotFoundException_if_organization_does_not_exist() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("No organization with key 'does_not_exist'"); + + ws.newRequest() + .setParam("organization", "does_not_exist") + .execute(); + } + + @Test + public void fail_on_paid_organization_when_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + db.rules().insert(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam("organization", organization.getKey()) + .executeProtobuf(Rules.SearchResponse.class); + } + private void insertRules() { RuleRepositoryDto repo1 = new RuleRepositoryDto("xoo", "xoo", "SonarQube"); RuleRepositoryDto repo2 = new RuleRepositoryDto("squid", "ws", "SonarQube"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java index 4296f924633..d18c61f35fc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/RuleQueryFactoryTest.java @@ -19,7 +19,6 @@ */ package org.sonar.server.rule.ws; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -32,13 +31,17 @@ import org.sonar.db.DbTester; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.rule.index.RuleQuery; +import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsAction; import org.sonar.server.ws.WsActionTester; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.rule.RuleStatus.DEPRECATED; import static org.sonar.api.rule.RuleStatus.READY; @@ -50,14 +53,15 @@ import static org.sonar.api.server.ws.WebService.Param.ASCENDING; import static org.sonar.api.server.ws.WebService.Param.SORT; import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.qualityprofile.ActiveRuleDto.INHERITED; import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVE_SEVERITIES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_AVAILABLE_SINCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_COMPARE_TO_PROFILE; -import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INCLUDE_EXTERNAL; +import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_INHERITANCE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_IS_TEMPLATE; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ORGANIZATION; @@ -74,21 +78,17 @@ public class RuleQueryFactoryTest { @Rule - public DbTester dbTester = DbTester.create(System2.INSTANCE); + public DbTester db = DbTester.create(System2.INSTANCE); @Rule public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); - private DbClient dbClient = dbTester.getDbClient(); + private DbClient dbClient = db.getDbClient(); - private RuleQueryFactory underTest = new RuleQueryFactory(dbClient, new RuleWsSupport(dbClient, null, TestDefaultOrganizationProvider.from(dbTester))); + private RuleQueryFactory underTest = new RuleQueryFactory(dbClient, new RuleWsSupport(dbClient, userSession, TestDefaultOrganizationProvider.from(db))); private FakeAction fakeAction = new FakeAction(underTest); - private OrganizationDto organization; - - @Before - public void before() { - organization = dbTester.organizations().insert(); - } @Test public void create_empty_query() { @@ -119,8 +119,9 @@ public void create_empty_query() { @Test public void create_rule_search_query() { - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); - QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); + QProfileDto compareToQualityProfile = db.qualityProfiles().insert(organization); RuleQuery result = executeRuleSearchQuery( PARAM_RULE_KEY, "ruleKey", @@ -152,19 +153,21 @@ public void create_rule_search_query() { @Test public void include_external_is_mandatory_for_rule_search_query() { - dbTester.qualityProfiles().insert(organization); - dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + db.qualityProfiles().insert(organization); + db.qualityProfiles().insert(organization); Request request = new SimpleGetRequest(); expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("The 'include_external' parameter is missing"); - underTest.createRuleSearchQuery(dbTester.getSession(), request); + underTest.createRuleSearchQuery(db.getSession(), request); } @Test public void create_query() { - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); - QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); + QProfileDto compareToQualityProfile = db.qualityProfiles().insert(organization); RuleQuery result = execute( PARAM_RULE_KEY, "ruleKey", @@ -194,33 +197,10 @@ public void create_query() { assertThat(result.includeExternal()).isFalse(); } - private void assertResult(RuleQuery result, QProfileDto qualityProfile, QProfileDto compareToQualityProfile) { - assertThat(result.getKey()).isEqualTo("ruleKey"); - - assertThat(result.getActivation()).isTrue(); - assertThat(result.getActiveSeverities()).containsOnly(MINOR, MAJOR); - assertThat(result.isAscendingSort()).isFalse(); - assertThat(result.getAvailableSinceLong()).isNotNull(); - assertThat(result.getInheritance()).containsOnly(INHERITED, OVERRIDES); - assertThat(result.isTemplate()).isTrue(); - assertThat(result.getLanguages()).containsOnly(qualityProfile.getLanguage()); - assertThat(result.getQueryText()).isEqualTo("S001"); - assertThat(result.getQProfile().getKee()).isEqualTo(qualityProfile.getKee()); - assertThat(result.getCompareToQProfile().getKee()).isEqualTo(compareToQualityProfile.getKee()); - assertThat(result.getOrganization().getUuid()).isEqualTo(organization.getUuid()); - assertThat(result.getRepositories()).containsOnly("pmd", "checkstyle"); - assertThat(result.getRuleKey()).isNull(); - assertThat(result.getSeverities()).containsOnly(MINOR, CRITICAL); - assertThat(result.getStatuses()).containsOnly(DEPRECATED, READY); - assertThat(result.getTags()).containsOnly("tag1", "tag2"); - assertThat(result.templateKey()).isEqualTo("architectural"); - assertThat(result.getTypes()).containsOnly(BUG, CODE_SMELL); - assertThat(result.getSortField()).isEqualTo("updatedAt"); - } - @Test public void use_quality_profiles_language_if_available() { - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); String qualityProfileKey = qualityProfile.getKee(); RuleQuery result = execute( @@ -241,7 +221,8 @@ public void use_specified_languages_if_no_quality_profile_available() { @Test public void create_query_add_language_from_profile() { - QProfileDto profile = dbTester.qualityProfiles().insert(organization, p -> p.setName("Sonar way").setLanguage("xoo").setKee("sonar-way")); + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setName("Sonar way").setLanguage("xoo").setKee("sonar-way")); RuleQuery result = execute( PARAM_QPROFILE, profile.getKee(), @@ -253,7 +234,8 @@ public void create_query_add_language_from_profile() { @Test public void filter_on_quality_profiles_organization_if_searching_for_actives_with_no_organization_specified() { - QProfileDto profile = dbTester.qualityProfiles().insert(organization, p -> p.setName("Sonar way").setLanguage("xoo").setKee("sonar-way")); + OrganizationDto organization = db.organizations().insert(); + QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setName("Sonar way").setLanguage("xoo").setKee("sonar-way")); RuleQuery result = execute( PARAM_ACTIVATION, "true", @@ -264,7 +246,8 @@ public void filter_on_quality_profiles_organization_if_searching_for_actives_wit @Test public void filter_on_compare_to() { - QProfileDto compareToProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto compareToProfile = db.qualityProfiles().insert(organization); RuleQuery result = execute( PARAM_ORGANIZATION, organization.getKey(), @@ -273,12 +256,36 @@ public void filter_on_compare_to() { assertThat(result.getCompareToQProfile().getKee()).isEqualTo(compareToProfile.getKee()); } + @Test + public void filter_by_qprofile_when_subscription_is_paid_and_user_is_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); + + RuleQuery result = execute(PARAM_QPROFILE, qualityProfile.getKee()); + + assertThat(result.getOrganization().getUuid()).isEqualTo(organization.getUuid()); + } + + @Test + public void filter_by_organization_when_subscription_is_paid_and_user_is_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); + + RuleQuery result = execute(PARAM_ORGANIZATION, organization.getKey()); + + assertThat(result.getOrganization().getUuid()).isEqualTo(organization.getUuid()); + } + @Test public void fail_if_organization_and_quality_profile_are_contradictory() { - OrganizationDto organization1 = dbTester.organizations().insert(); - OrganizationDto organization2 = dbTester.organizations().insert(); + OrganizationDto organization1 = db.organizations().insert(); + OrganizationDto organization2 = db.organizations().insert(); - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization1); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization1); String qualityProfileKey = qualityProfile.getKee(); String organization2Key = organization2.getKey(); @@ -292,11 +299,11 @@ public void fail_if_organization_and_quality_profile_are_contradictory() { @Test public void fail_if_organization_and_compare_to_quality_profile_are_contradictory() { - OrganizationDto organization = dbTester.organizations().insert(); - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); - OrganizationDto otherOrganization = dbTester.organizations().insert(); - QProfileDto compareToQualityProfile = dbTester.qualityProfiles().insert(otherOrganization); + OrganizationDto otherOrganization = db.organizations().insert(); + QProfileDto compareToQualityProfile = db.qualityProfiles().insert(otherOrganization); expectedException.expect(IllegalArgumentException.class); expectedException @@ -309,7 +316,8 @@ public void fail_if_organization_and_compare_to_quality_profile_are_contradictor @Test public void fail_when_organization_does_not_exist() { - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); String qualityProfileKey = qualityProfile.getKee(); expectedException.expect(NotFoundException.class); @@ -329,7 +337,8 @@ public void fail_when_profile_does_not_exist() { @Test public void fail_when_compare_to_profile_does_not_exist() { - QProfileDto qualityProfile = dbTester.qualityProfiles().insert(organization); + OrganizationDto organization = db.organizations().insert(); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); expectedException.expect(NotFoundException.class); expectedException.expectMessage("The specified qualityProfile 'unknown' does not exist"); @@ -338,6 +347,51 @@ public void fail_when_compare_to_profile_does_not_exist() { PARAM_COMPARE_TO_PROFILE, "unknown"); } + @Test + public void fail_when_searching_by_organization_when_subscription_is_paid_and_user_is_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + execute(PARAM_ORGANIZATION, organization.getKey()); + } + + @Test + public void fail_when_searching_by_qprofile_when_subscription_is_paid_and_user_is_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + QProfileDto qualityProfile = db.qualityProfiles().insert(organization); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + execute(PARAM_QPROFILE, qualityProfile.getKee()); + } + + private void assertResult(RuleQuery result, QProfileDto qualityProfile, QProfileDto compareToQualityProfile) { + assertThat(result.getKey()).isEqualTo("ruleKey"); + + assertThat(result.getActivation()).isTrue(); + assertThat(result.getActiveSeverities()).containsOnly(MINOR, MAJOR); + assertThat(result.isAscendingSort()).isFalse(); + assertThat(result.getAvailableSinceLong()).isNotNull(); + assertThat(result.getInheritance()).containsOnly(INHERITED, OVERRIDES); + assertThat(result.isTemplate()).isTrue(); + assertThat(result.getLanguages()).containsOnly(qualityProfile.getLanguage()); + assertThat(result.getQueryText()).isEqualTo("S001"); + assertThat(result.getQProfile().getKee()).isEqualTo(qualityProfile.getKee()); + assertThat(result.getCompareToQProfile().getKee()).isEqualTo(compareToQualityProfile.getKee()); + assertThat(result.getOrganization().getUuid()).isEqualTo(qualityProfile.getOrganizationUuid()); + assertThat(result.getRepositories()).containsOnly("pmd", "checkstyle"); + assertThat(result.getRuleKey()).isNull(); + assertThat(result.getSeverities()).containsOnly(MINOR, CRITICAL); + assertThat(result.getStatuses()).containsOnly(DEPRECATED, READY); + assertThat(result.getTags()).containsOnly("tag1", "tag2"); + assertThat(result.templateKey()).isEqualTo("architectural"); + assertThat(result.getTypes()).containsOnly(BUG, CODE_SMELL); + assertThat(result.getSortField()).isEqualTo("updatedAt"); + } + private RuleQuery execute(String... paramsKeyAndValue) { WsActionTester ws = new WsActionTester(fakeAction); TestRequest request = ws.newRequest(); @@ -354,7 +408,7 @@ private RuleQuery executeRuleSearchQuery(String... paramsKeyAndValue) { request.setParam(paramsKeyAndValue[i], paramsKeyAndValue[i + 1]); } - return underTest.createRuleSearchQuery(dbTester.getSession(), request); + return underTest.createRuleSearchQuery(db.getSession(), request); } private class FakeAction implements WsAction { @@ -375,7 +429,7 @@ public void define(WebService.NewController controller) { @Override public void handle(Request request, Response response) { - ruleQuery = ruleQueryFactory.createRuleQuery(dbTester.getSession(), request); + ruleQuery = ruleQueryFactory.createRuleQuery(db.getSession(), request); } RuleQuery getRuleQuery() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java index 61cade0fb54..6114ac6ab6e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java @@ -45,6 +45,7 @@ import org.sonar.db.rule.RuleParamDto; import org.sonar.db.user.UserDto; import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.language.LanguageTesting; import org.sonar.server.organization.DefaultOrganizationProvider; @@ -69,6 +70,7 @@ import org.sonarqube.ws.Rules.Rule; import org.sonarqube.ws.Rules.SearchResponse; +import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Arrays.stream; import static java.util.Collections.singleton; @@ -81,6 +83,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.sonar.api.rule.Severity.BLOCKER; +import static org.sonar.db.organization.OrganizationDto.Subscription.PAID; import static org.sonar.db.rule.RuleTesting.setSystemTags; import static org.sonar.db.rule.RuleTesting.setTags; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; @@ -440,7 +443,7 @@ public void should_not_return_tags_of_foreign_organization() { } @Test - public void should_return_specified_fields() throws Exception { + public void should_return_specified_fields() { RuleDefinitionDto rule = createJavaRule(); indexRules(); @@ -918,6 +921,37 @@ public void compare_to_another_profile() { .containsExactlyInAnyOrder(anotherProfileRule1.getKey().toString(), anotherProfileRule2.getKey().toString()); } + @Test + public void returns_rules_on_paid_organization() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + RuleDefinitionDto rule = db.rules().insert(); + indexRules(); + UserDto user = db.users().insertUser(); + userSession.logIn(user).addMembership(organization); + + SearchResponse result = ws.newRequest() + .setParam("organization", organization.getKey()) + .executeProtobuf(SearchResponse.class); + + assertThat(result.getRulesList()) + .extracting(Rule::getKey) + .containsExactlyInAnyOrder(rule.getKey().toString()); + } + + @Test + public void fail_on_paid_organization_when_not_member() { + OrganizationDto organization = db.organizations().insert(o -> o.setSubscription(PAID)); + db.rules().insert(); + indexRules(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(format("You're not member of organization '%s'", organization.getKey())); + + ws.newRequest() + .setParam("organization", organization.getKey()) + .executeProtobuf(SearchResponse.class); + } + @SafeVarargs private final void checkField(RuleDefinitionDto rule, String fieldName, Extractor responseExtractor, T... expected) { SearchResponse result = ws.newRequest() diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/AbstractMockUserSession.java b/server/sonar-server/src/test/java/org/sonar/server/tester/AbstractMockUserSession.java index c252d0822e7..942f2336cef 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/AbstractMockUserSession.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/AbstractMockUserSession.java @@ -28,6 +28,7 @@ import org.sonar.api.web.UserRole; import org.sonar.core.permission.ProjectPermissions; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; import org.sonar.server.user.AbstractUserSession; @@ -39,7 +40,8 @@ public abstract class AbstractMockUserSession private HashMultimap projectUuidByPermission = HashMultimap.create(); private final HashMultimap permissionsByOrganizationUuid = HashMultimap.create(); private Map projectUuidByComponentUuid = newHashMap(); - private Set projectPermissionsCheckedByUuid = new HashSet<>(); + private Set projectPermissions = new HashSet<>(); + private Set organizationMembership = new HashSet<>(); private boolean systemAdministrator = false; protected AbstractMockUserSession(Class clazz) { @@ -66,8 +68,8 @@ public T registerComponents(ComponentDto... components) { if (component.projectUuid().equals(component.uuid()) && !component.isPrivate()) { this.projectUuidByPermission.put(UserRole.USER, component.uuid()); this.projectUuidByPermission.put(UserRole.CODEVIEWER, component.uuid()); - this.projectPermissionsCheckedByUuid.add(UserRole.USER); - this.projectPermissionsCheckedByUuid.add(UserRole.CODEVIEWER); + this.projectPermissions.add(UserRole.USER); + this.projectPermissions.add(UserRole.CODEVIEWER); } this.projectUuidByComponentUuid.put(component.uuid(), component.projectUuid()); }); @@ -81,7 +83,7 @@ public T addProjectPermission(String permission, ComponentDto... components) { "public component %s can't be granted public permission %s", component.uuid(), permission); }); registerComponents(components); - this.projectPermissionsCheckedByUuid.add(permission); + this.projectPermissions.add(permission); Arrays.stream(components) .forEach(component -> this.projectUuidByPermission.put(permission, component.projectUuid())); return clazz.cast(this); @@ -94,7 +96,7 @@ protected Optional componentUuidToProjectUuid(String componentUuid) { @Override protected boolean hasProjectUuidPermission(String permission, String projectUuid) { - return projectPermissionsCheckedByUuid.contains(permission) && projectUuidByPermission.get(permission).contains(projectUuid); + return projectPermissions.contains(permission) && projectUuidByPermission.get(permission).contains(projectUuid); } public T setSystemAdministrator(boolean b) { @@ -106,4 +108,14 @@ public T setSystemAdministrator(boolean b) { public boolean isSystemAdministrator() { return isRoot() || systemAdministrator; } + + @Override + protected boolean hasMembershipImpl(OrganizationDto organization) { + return organizationMembership.contains(organization.getUuid()); + } + + public void addOrganizationMembership(OrganizationDto organization) { + this.organizationMembership.add(organization.getUuid()); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java b/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java index d3d535e13c9..98af9348a66 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/AnonymousMockUserSession.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Collections; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.user.GroupDto; public class AnonymousMockUserSession extends AbstractMockUserSession { @@ -62,4 +63,9 @@ public boolean isLoggedIn() { public Collection getGroups() { return Collections.emptyList(); } + + @Override + public boolean hasMembershipImpl(OrganizationDto organization) { + return false; + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java b/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java index 90aae92ae16..91afb2ea4dd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/MockUserSession.java @@ -113,4 +113,5 @@ public MockUserSession setGroups(GroupDto... groups) { this.groups = asList(groups); return this; } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java index 2c9418aefd8..a3df41abed9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/UserSessionRule.java @@ -333,4 +333,21 @@ public UserSession checkIsSystemAdministrator() { currentUserSession.checkIsSystemAdministrator(); return this; } + + @Override + public boolean hasMembership(OrganizationDto organization) { + return currentUserSession.hasMembership(organization); + } + + @Override + public UserSession checkMembership(OrganizationDto organization) { + currentUserSession.checkMembership(organization); + return this; + } + + public UserSessionRule addMembership(OrganizationDto organization) { + ensureAbstractMockUserSession().addOrganizationMembership(organization); + return this; + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/DoPrivilegedTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/DoPrivilegedTest.java index 76f65454665..d1e57065cda 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/DoPrivilegedTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/DoPrivilegedTest.java @@ -26,6 +26,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class DoPrivilegedTest { @@ -40,7 +41,7 @@ public void setUp() { } @Test - public void should_allow_everything_in_privileged_block_only() { + public void allow_everything_in_privileged_block_only() { UserSessionCatcherTask catcher = new UserSessionCatcherTask(); DoPrivileged.execute(catcher); @@ -49,13 +50,14 @@ public void should_allow_everything_in_privileged_block_only() { assertThat(catcher.userSession.isLoggedIn()).isFalse(); assertThat(catcher.userSession.hasComponentPermission("any permission", new ComponentDto())).isTrue(); assertThat(catcher.userSession.isSystemAdministrator()).isTrue(); + assertThat(catcher.userSession.hasMembership(newOrganizationDto())).isTrue(); // verify session in place after task is done assertThat(threadLocalUserSession.get()).isSameAs(session); } @Test - public void should_loose_privileges_on_exception() { + public void loose_privileges_on_exception() { UserSessionCatcherTask catcher = new UserSessionCatcherTask() { @Override protected void doPrivileged() { @@ -74,6 +76,7 @@ protected void doPrivileged() { // verify the session used inside Privileged task assertThat(catcher.userSession.isLoggedIn()).isFalse(); assertThat(catcher.userSession.hasComponentPermission("any permission", new ComponentDto())).isTrue(); + assertThat(catcher.userSession.hasMembership(newOrganizationDto())).isTrue(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java index 28eae3aaaa2..d1e865d81d4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/ServerUserSessionTest.java @@ -639,6 +639,62 @@ public void hasComponentPermission_on_branch_checks_permissions_of_its_project() assertThat(hasComponentPermissionByDtoOrUuid(underTest, "p1", fileInBranch)).isTrue(); } + @Test + public void hasMembership() { + OrganizationDto organization = db.organizations().insert(); + UserDto notMember = db.users().insertUser(); + UserDto member = db.users().insertUser(); + db.organizations().addMember(organization, member); + UserDto root = db.users().makeRoot(db.users().insertUser()); + + assertThat(newUserSession(member).hasMembership(organization)).isTrue(); + assertThat(newUserSession(notMember).hasMembership(organization)).isFalse(); + assertThat(newUserSession(root).hasMembership(organization)).isTrue(); + } + + @Test + public void hasMembership_keeps_membership_in_cache() { + OrganizationDto organization = db.organizations().insert(); + UserDto user = db.users().insertUser(); + db.organizations().addMember(organization, user); + + ServerUserSession session = newUserSession(user); + assertThat(session.hasMembership(organization)).isTrue(); + + // membership updated but not cache + db.getDbClient().organizationMemberDao().delete(db.getSession(), organization.getUuid(), user.getId()); + db.commit(); + assertThat(session.hasMembership(organization)).isTrue(); + } + + @Test + public void checkMembership_throws_ForbiddenException_when_user_is_not_member_of_organization() { + OrganizationDto organization = db.organizations().insert(); + UserDto notMember = db.users().insertUser(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage(String.format("You're not member of organization '%s'", organization.getKey())); + + newUserSession(notMember).checkMembership(organization); + } + + @Test + public void checkMembership_succeeds_when_user_is_member_of_organization() { + OrganizationDto organization = db.organizations().insert(); + UserDto member = db.users().insertUser(); + db.organizations().addMember(organization, member); + + newUserSession(member).checkMembership(organization); + } + + @Test + public void checkMembership_succeeds_when_user_is_not_member_of_organization_but_root() { + OrganizationDto organization = db.organizations().insert(); + UserDto root = db.users().makeRoot(db.users().insertUser()); + + newUserSession(root).checkMembership(organization); + } + private ServerUserSession newUserSession(@Nullable UserDto userDto) { return new ServerUserSession(dbClient, organizationFlags, defaultOrganizationProvider, userDto); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java b/server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java index 09aa329b1f9..d714a8d0262 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/TestUserSessionFactory.java @@ -22,9 +22,10 @@ import java.util.Collection; import java.util.Optional; import javax.annotation.Nullable; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; -import org.sonar.db.permission.OrganizationPermission; import static java.util.Objects.requireNonNull; @@ -116,6 +117,11 @@ public boolean isSystemAdministrator() { throw notImplemented(); } + @Override + public boolean hasMembershipImpl(OrganizationDto organization) { + throw notImplemented(); + } + private static RuntimeException notImplemented() { return new UnsupportedOperationException("not implemented"); }