diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2018-11-20 21:30:41 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-11-21 20:20:57 +0100 |
commit | 5788d993b8417cec4f346e728c575341145b20ad (patch) | |
tree | f4a5f1415535ba14979131521a588ed80cd403a9 /server/sonar-server/src | |
parent | 4a1aaab8cc4d0db84f06389093b7bfa3d8e70abd (diff) | |
download | sonarqube-5788d993b8417cec4f346e728c575341145b20ad.tar.gz sonarqube-5788d993b8417cec4f346e728c575341145b20ad.zip |
SONARCLOUD-172 SONARCLOUD-188 fix access to secured settings
Diffstat (limited to 'server/sonar-server/src')
3 files changed, 86 insertions, 19 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java index f3decab26bd..906c0492526 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java @@ -73,8 +73,12 @@ public class SettingsWsSupport { return hasPermission(OrganizationPermission.SCAN, SCAN_EXECUTION, component) || (verifySecuredSetting(key, definition, component) && (verifyLicenseSetting(key, definition))); } + static boolean isSecured(String key) { + return key.endsWith(DOT_SECURED); + } + private boolean verifySecuredSetting(String key, @Nullable PropertyDefinition definition, Optional<ComponentDto> component) { - return isLicense(key, definition) || (!key.endsWith(DOT_SECURED) || hasPermission(OrganizationPermission.ADMINISTER, ADMIN, component)); + return isLicense(key, definition) || (!isSecured(key) || hasPermission(OrganizationPermission.ADMINISTER, ADMIN, component)); } private boolean verifyLicenseSetting(String key, @Nullable PropertyDefinition definition) { @@ -86,11 +90,14 @@ public class SettingsWsSupport { } private boolean hasPermission(OrganizationPermission orgPermission, String projectPermission, Optional<ComponentDto> component) { + if (userSession.isSystemAdministrator()) { + return true; + } if (userSession.hasPermission(orgPermission, defaultOrganizationProvider.get().getUuid())) { return true; } return component - .map(c -> userSession.hasComponentPermission(projectPermission, c)) + .map(c -> userSession.hasPermission(orgPermission, c.getOrganizationUuid()) || userSession.hasComponentPermission(projectPermission, c)) .orElse(false); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java index 66ad5d203eb..430f3c6e23f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java @@ -35,6 +35,7 @@ import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.sonar.api.config.Configuration; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.server.ws.Change; @@ -60,11 +61,13 @@ import static org.sonar.api.CoreProperties.SERVER_STARTTIME; import static org.sonar.api.PropertyType.PROPERTY_SET; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION; +import static org.sonar.process.ProcessProperties.Property.SONARCLOUD_ENABLED; import static org.sonar.server.setting.ws.PropertySetExtractor.extractPropertySetKeys; import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH; import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT; import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_KEYS; import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST; +import static org.sonar.server.setting.ws.SettingsWsSupport.isSecured; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.WsUtils.writeProtobuf; @@ -81,14 +84,16 @@ public class ValuesAction implements SettingsWsAction { private final UserSession userSession; private final PropertyDefinitions propertyDefinitions; private final SettingsWsSupport settingsWsSupport; + private final boolean isSonarCloud; public ValuesAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, PropertyDefinitions propertyDefinitions, - SettingsWsSupport settingsWsSupport) { + SettingsWsSupport settingsWsSupport, Configuration configuration) { this.dbClient = dbClient; this.componentFinder = componentFinder; this.userSession = userSession; this.propertyDefinitions = propertyDefinitions; this.settingsWsSupport = settingsWsSupport; + this.isSonarCloud = configuration.getBoolean(SONARCLOUD_ENABLED.getKey()).orElse(false); } @Override @@ -194,7 +199,14 @@ public class ValuesAction implements SettingsWsAction { } private List<Setting> loadGlobalSettings(DbSession dbSession, Set<String> keys) { - List<PropertyDto> properties = dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, keys); + Set<String> allowedKeys; + if (isSonarCloud && !userSession.isSystemAdministrator()) { + // remove the global settings that require admin permission + allowedKeys = keys.stream().filter(k -> !isSecured(k)).collect(Collectors.toSet()); + } else { + allowedKeys = keys; + } + List<PropertyDto> properties = dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, allowedKeys); List<PropertyDto> propertySets = dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, getPropertySetKeys(properties)); return properties.stream() .map(property -> Setting.createFromDto(property, getPropertySets(property.getKey(), propertySets, null), propertyDefinitions.get(property.getKey()))) diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java index 7d23581c158..8b0ec0a4a0a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java @@ -28,9 +28,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.PropertyType; +import org.sonar.api.config.Configuration; import org.sonar.api.config.PropertyDefinition; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.PropertyFieldDefinition; +import org.sonar.api.config.internal.MapSettings; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; @@ -40,6 +42,7 @@ import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.permission.OrganizationPermission; import org.sonar.db.property.PropertyDbTester; import org.sonar.process.ProcessProperties; import org.sonar.server.component.TestComponentFinder; @@ -66,10 +69,10 @@ import static org.sonar.api.web.UserRole.CODEVIEWER; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION; import static org.sonar.db.component.ComponentTesting.newModuleDto; -import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.db.permission.OrganizationPermission.SCAN; import static org.sonar.db.property.PropertyTesting.newComponentPropertyDto; import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto; +import static org.sonar.process.ProcessProperties.Property.SONARCLOUD_ENABLED; import static org.sonarqube.ws.MediaTypes.JSON; import static org.sonarqube.ws.Settings.Setting.ParentValueOneOfCase.PARENTVALUEONEOF_NOT_SET; @@ -92,9 +95,6 @@ public class ValuesActionTest { private SettingsWsSupport support = new SettingsWsSupport(defaultOrganizationProvider, userSession); private ComponentDto project; - private WsActionTester ws = new WsActionTester( - new ValuesAction(dbClient, TestComponentFinder.from(db), userSession, definitions, support)); - @Before public void setUp() throws Exception { OrganizationDto organizationDto = db.organizations().insert(); @@ -733,7 +733,7 @@ public class ValuesActionTest { definitions.addComponent(PropertyDefinition.builder("sonar.leak.period").onQualifiers(PROJECT).build()); propertyDb.insertProperties(newComponentPropertyDto(branch).setKey("sonar.leak.period").setValue("two")); - ValuesWsResponse result = ws.newRequest() + ValuesWsResponse result = newTester().newRequest() .setParam("keys", "sonar.leak.period") .setParam("component", branch.getKey()) .setParam("branch", branch.getBranch()) @@ -751,7 +751,7 @@ public class ValuesActionTest { definitions.addComponent(PropertyDefinition.builder("sonar.leak.period").onQualifiers(PROJECT).build()); propertyDb.insertProperties(newComponentPropertyDto(project).setKey("sonar.leak.period").setValue("two")); - ValuesWsResponse result = ws.newRequest() + ValuesWsResponse result = newTester().newRequest() .setParam("keys", "sonar.leak.period") .setParam("component", branch.getKey()) .setParam("branch", branch.getBranch()) @@ -791,7 +791,7 @@ public class ValuesActionTest { expectedException.expect(NotFoundException.class); expectedException.expectMessage("Component key 'unknown' not found"); - ws.newRequest() + newTester().newRequest() .setParam("keys", "foo") .setParam("component", "unknown") .execute(); @@ -807,7 +807,7 @@ public class ValuesActionTest { expectedException.expect(NotFoundException.class); expectedException.expectMessage(format("Component '%s' on branch 'unknown' not found", branch.getKey())); - ws.newRequest() + newTester().newRequest() .setParam("keys", settingKey) .setParam("component", branch.getKey()) .setParam("branch", "unknown") @@ -834,13 +834,13 @@ public class ValuesActionTest { .build()); propertyDb.insertPropertySet("sonar.demo", null, ImmutableMap.of("text", "foo", "boolean", "true"), ImmutableMap.of("text", "bar", "boolean", "false")); - String result = ws.newRequest() + String result = newTester().newRequest() .setParam("keys", "sonar.test.jira,sonar.autogenerated,sonar.demo") .setMediaType(JSON) .execute() .getInput(); - JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result); + JsonAssert.assertJson(newTester().getDef().responseExampleAsString()).isSimilarTo(result); } @Test @@ -853,7 +853,7 @@ public class ValuesActionTest { expectedException.expect(NotFoundException.class); expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey())); - ws.newRequest() + newTester().newRequest() .setParam("keys", "foo") .setParam("component", branch.getDbKey()) .execute(); @@ -868,7 +868,7 @@ public class ValuesActionTest { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage(format("Setting '%s' can only be used in sonar.properties", settingKey)); - ws.newRequest() + newTester().newRequest() .setParam("keys", settingKey) .setParam("component", project.getKey()) .execute(); @@ -876,7 +876,7 @@ public class ValuesActionTest { @Test public void test_ws_definition() { - WebService.Action action = ws.getDef(); + WebService.Action action = newTester().getDef(); assertThat(action).isNotNull(); assertThat(action.isInternal()).isFalse(); assertThat(action.isPost()).isFalse(); @@ -884,6 +884,38 @@ public class ValuesActionTest { assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("keys", "component", "branch", "pullRequest"); } + @Test + public void sonarcloud_global_secured_properties_require_system_admin_permission() { + PropertyDefinition securedDef = PropertyDefinition.builder("my.password.secured").build(); + PropertyDefinition standardDef = PropertyDefinition.builder("my.property").build(); + definitions.addComponents(asList(securedDef, standardDef)); + propertyDb.insertProperties( + newGlobalPropertyDto().setKey(securedDef.key()).setValue("securedValue"), + newGlobalPropertyDto().setKey(standardDef.key()).setValue("standardValue")); + + // anonymous + WsActionTester tester = newSonarCloudTester(); + ValuesWsResponse response = executeRequest(tester, null, securedDef.key(), standardDef.key()); + assertThat(response.getSettingsList()).extracting(Settings.Setting::getValue).containsExactly("standardValue"); + + // organization administrator but not system administrator + userSession.logIn() + .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization()); + response = executeRequest(tester, null, securedDef.key(), standardDef.key()); + assertThat(response.getSettingsList()).extracting(Settings.Setting::getValue).containsExactly("standardValue"); + + // organization administrator + userSession.logIn() + .addPermission(OrganizationPermission.ADMINISTER, db.getDefaultOrganization()); + response = executeRequest(tester, null, securedDef.key(), standardDef.key()); + assertThat(response.getSettingsList()).extracting(Settings.Setting::getValue).containsExactly("standardValue"); + + // system administrator + userSession.logIn().setSystemAdministrator(); + response = executeRequest(tester, null, securedDef.key(), standardDef.key()); + assertThat(response.getSettingsList()).extracting(Settings.Setting::getValue).containsExactlyInAnyOrder("securedValue", "standardValue"); + } + private ValuesWsResponse executeRequestForComponentProperties(ComponentDto componentDto, String... keys) { return executeRequest(componentDto.getDbKey(), keys); } @@ -897,7 +929,11 @@ public class ValuesActionTest { } private ValuesWsResponse executeRequest(@Nullable String componentKey, String... keys) { - TestRequest request = ws.newRequest(); + return executeRequest(newTester(), componentKey, keys); + } + + private ValuesWsResponse executeRequest(WsActionTester tester, @Nullable String componentKey, String... keys) { + TestRequest request = tester.newRequest(); if (keys.length > 0) { request.setParam("keys", COMMA_JOINER.join(keys)); } @@ -916,7 +952,7 @@ public class ValuesActionTest { } private void logInAsAdmin() { - userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization()); + userSession.logIn().setSystemAdministrator(); } private void logInAsProjectAdmin() { @@ -968,4 +1004,16 @@ public class ValuesActionTest { } } } + + private WsActionTester newTester() { + MapSettings settings = new MapSettings(); + Configuration configuration = settings.asConfig(); + return new WsActionTester(new ValuesAction(dbClient, TestComponentFinder.from(db), userSession, definitions, support, configuration)); + } + + private WsActionTester newSonarCloudTester() { + MapSettings settings = new MapSettings().setProperty(SONARCLOUD_ENABLED.getKey(), "true"); + Configuration configuration = settings.asConfig(); + return new WsActionTester(new ValuesAction(dbClient, TestComponentFinder.from(db), userSession, definitions, support, configuration)); + } } |