aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.gradle2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionDao.java8
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java4
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml31
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java59
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java4
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/DbVersion98.java1
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescription.java40
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java6
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest.java79
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest/schema.sql9
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/List.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap5
-rw-r--r--server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx10
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartup.java75
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartupTest.java152
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/groups-example.json2
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/template/template_groups-example.json2
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/TemplateGroupsActionTest.java2
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java3
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
21 files changed, 485 insertions, 12 deletions
diff --git a/build.gradle b/build.gradle
index 6313a872b1e..726da0b4fe6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -340,7 +340,7 @@ subprojects {
dependency 'org.postgresql:postgresql:42.5.0'
dependency 'org.reflections:reflections:0.10.2'
dependency 'org.simpleframework:simple:5.1.6'
- dependency 'org.sonarsource.orchestrator:sonar-orchestrator:3.39.0.167'
+ dependency 'org.sonarsource.orchestrator:sonar-orchestrator:3.40.0.183'
dependency 'org.sonarsource.update-center:sonar-update-center-common:1.29.0.1000'
dependency('org.springframework:spring-context:5.3.23') {
exclude 'commons-logging:commons-logging'
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionDao.java
index c23b174baee..3de87b133e1 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionDao.java
@@ -73,6 +73,14 @@ public class GroupPermissionDao implements Dao {
return executeLargeInputs(groupUuids, groups -> mapper(dbSession).selectByGroupUuids(groups, projectUuid));
}
+ public List<String> selectProjectKeysWithAnyonePermissions(DbSession dbSession, int max) {
+ return mapper(dbSession).selectProjectKeysWithAnyonePermissions(max);
+ }
+
+ public int countProjectsWithAnyonePermissions(DbSession dbSession) {
+ return mapper(dbSession).countProjectsWithAnyonePermissions();
+ }
+
/**
* Select global and project permissions of a given group (Anyone group is NOT supported)
* Each row returns a {@link GroupPermissionDto}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java
index a97740da6af..bcb9705b655 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java
@@ -37,6 +37,10 @@ public interface GroupPermissionMapper {
void groupsCountByProjectUuidAndPermission(Map<String, Object> parameters, ResultHandler<CountPerProjectPermission> resultHandler);
+ List<String> selectProjectKeysWithAnyonePermissions(int max);
+
+ int countProjectsWithAnyonePermissions();
+
void insert(GroupPermissionDto dto);
int delete(@Param("permission") String permission, @Nullable @Param("groupUuid") String groupUuid, @Nullable @Param("rootComponentUuid") String rootComponentUuid);
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml
index 1ce169e2918..d062673b736 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml
@@ -39,6 +39,37 @@
groups.componentUuid
</select>
+ <select id="selectProjectKeysWithAnyonePermissions" parameterType="int" resultType="string">
+ select distinct(p.kee) as kee from group_roles gr
+ left join projects p on gr.component_uuid = p.uuid
+ where gr.group_uuid is null and gr.component_uuid is not null
+ ORDER BY kee ASC
+ limit #{max}
+ </select>
+
+ <!-- Oracle -->
+ <select id="selectProjectKeysWithAnyonePermissions" parameterType="int" resultType="string" databaseId="oracle">
+ select * from (select distinct p.kee as kee from group_roles gr
+ left join projects p on gr.component_uuid = p.uuid
+ where gr.group_uuid is null and gr.component_uuid is not null
+ ORDER BY kee ASC
+ )
+ where rownum &lt;= #{max}
+ </select>
+
+ <!-- SQL Server -->
+ <select id="selectProjectKeysWithAnyonePermissions" parameterType="int" resultType="string" databaseId="mssql">
+ select distinct top(#{max}) p.kee as kee from group_roles gr
+ left join projects p on gr.component_uuid = p.uuid
+ where gr.group_uuid is null and gr.component_uuid is not null
+ ORDER BY kee ASC
+ </select>
+
+ <select id="countProjectsWithAnyonePermissions" resultType="int">
+ select count(distinct(gr.component_uuid))
+ from group_roles gr where gr.group_uuid is null and gr.component_uuid is not null
+ </select>
+
<select id="selectGroupNamesByQuery" parameterType="map" resultType="string">
select sub.name, lower(sub.name), sub.groupUuid
<include refid="groupsByQuery"/>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java
index 33fc0b5dd3e..ea7679c9e78 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java
@@ -438,6 +438,65 @@ public class GroupPermissionDaoTest {
}
@Test
+ public void selectProjectKeysWithAnyonePermissions_on_public_project_none_found() {
+ ComponentDto project1 = db.components().insertPublicProject();
+ ComponentDto project2 = db.components().insertPublicProject();
+ GroupDto group = db.users().insertGroup();
+ db.users().insertProjectPermissionOnGroup(group, "perm1", project1);
+ db.users().insertProjectPermissionOnGroup(group, "perm1", project2);
+ assertThat(underTest.selectProjectKeysWithAnyonePermissions(dbSession, 3)).isEmpty();
+ }
+
+ @Test
+ public void selectProjectKeysWithAnyonePermissions_on_public_project_ordered_by_kee() {
+ ComponentDto project1 = db.components().insertPublicProject();
+ ComponentDto project2 = db.components().insertPublicProject();
+ ComponentDto project3 = db.components().insertPublicProject();
+ db.users().insertProjectPermissionOnAnyone("perm1", project1);
+ db.users().insertProjectPermissionOnAnyone("perm1", project2);
+ db.users().insertProjectPermissionOnAnyone("perm1", project3);
+ assertThat(underTest.selectProjectKeysWithAnyonePermissions(dbSession, 3))
+ .containsExactly(project1.getKey(), project2.getKey(), project3.getKey());
+ }
+
+ @Test
+ public void selectProjectKeysWithAnyonePermissions_on_public_project_ordered_by_kee_max_5() {
+ IntStream.rangeClosed(1, 9).forEach(i -> {
+ ComponentDto project = db.components().insertPublicProject(p -> p.setKey("key-" + i));
+ db.users().insertProjectPermissionOnAnyone("perm-" + i, project);
+ });
+
+ assertThat(underTest.selectProjectKeysWithAnyonePermissions(dbSession, 5))
+ .containsExactly("key-1", "key-2", "key-3", "key-4", "key-5");
+ }
+
+ @Test
+ public void selectProjectKeysWithAnyonePermissions_on_public_projects_omit_blanket_anyone_group_permissions() {
+ // Although saved in the same table (group_roles), this should not be included in the result as not assigned to single project.
+ db.users().insertPermissionOnAnyone("perm-anyone");
+
+ IntStream.rangeClosed(1, 9).forEach(i -> {
+ ComponentDto project = db.components().insertPublicProject(p -> p.setKey("key-" + i));
+ db.users().insertProjectPermissionOnAnyone("perm-" + i, project);
+ });
+
+ assertThat(underTest.selectProjectKeysWithAnyonePermissions(dbSession, 5))
+ .containsExactly("key-1", "key-2", "key-3", "key-4", "key-5");
+ }
+
+ @Test
+ public void countProjectsWithAnyonePermissions() {
+ GroupDto group = db.users().insertGroup();
+ IntStream.rangeClosed(1, 5).forEach(i -> {
+ ComponentDto project = db.components().insertPublicProject(p -> p.setKey("key-" + i));
+ db.users().insertProjectPermissionOnAnyone("perm-" + i, project);
+ db.users().insertProjectPermissionOnGroup(group, "perm-", project);
+ });
+
+ assertThat(underTest.countProjectsWithAnyonePermissions(dbSession)).isEqualTo(5);
+ }
+
+ @Test
public void selectProjectPermissionsOfGroup_on_private_project() {
GroupDto group1 = db.users().insertGroup("group1");
ComponentDto project1 = db.components().insertPrivateProject();
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
index 762a1c14213..c374e62fa84 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchema.java
@@ -169,7 +169,7 @@ public class PopulateInitialSchema extends DataChange {
upsert
.setString(1, uuidFactory.create())
.setString(2, USERS_GROUP)
- .setString(3, "Any new users created will automatically join this group")
+ .setString(3, "Every authenticated user automatically belongs to this group")
.setDate(4, now)
.setDate(5, now)
.addBatch();
@@ -235,7 +235,7 @@ public class PopulateInitialSchema extends DataChange {
for (String anyoneRole : Arrays.asList("scan", "provisioning")) {
upsert
.setString(1, uuidFactory.create())
- .setString(2, null)
+ .setString(2, groups.getUserGroupUuid())
.setString(3, anyoneRole)
.addBatch();
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/DbVersion98.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/DbVersion98.java
index f792305f9c7..ab5da5a55db 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/DbVersion98.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/DbVersion98.java
@@ -30,6 +30,7 @@ public class DbVersion98 implements DbVersion {
.add(6701, "Drop live measure variation column", DropLiveMeasureVariationColumn.class)
.add(6702, "Move project measure variations to values", MoveProjectMeasureVariationToValue.class)
.add(6703, "Drop project measure variation column", DropProjectMeasureVariationColumn.class)
+ .add(6704, "Update sonar-users group description", UpsertSonarUsersDescription.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescription.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescription.java
new file mode 100644
index 00000000000..52b72475631
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescription.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v98;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.Upsert;
+
+public class UpsertSonarUsersDescription extends DataChange {
+
+ public UpsertSonarUsersDescription(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ Upsert upsert = context.prepareUpsert("update groups set description = ? where name = 'sonar-users'");
+ upsert.setString(1, "Every authenticated user automatically belongs to this group");
+ upsert.execute();
+ upsert.commit();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java
index ad8bc02f549..909159f0a09 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v00/PopulateInitialSchemaTest.java
@@ -67,7 +67,7 @@ public class PopulateInitialSchemaTest {
underTest.execute();
verifyAdminUser();
- verifyGroup("sonar-users", "Any new users created will automatically join this group");
+ verifyGroup("sonar-users", "Every authenticated user automatically belongs to this group");
verifyGroup("sonar-administrators", "System administrators");
String qualityGateUuid = verifyQualityGate();
verifyInternalProperties();
@@ -204,7 +204,7 @@ public class PopulateInitialSchemaTest {
}
private void verifyRolesOfUsersGroup() {
- assertThat(selectRoles("sonar-users")).isEmpty();
+ assertThat(selectRoles("sonar-users")).containsOnly("provisioning", "scan");
}
private void verifyRolesOfAnyone() {
@@ -212,7 +212,7 @@ public class PopulateInitialSchemaTest {
"from group_roles gr where gr.group_uuid is null");
Stream<String> roles = rows.stream()
.map(row -> (String) row.get("role"));
- assertThat(roles).containsOnly("provisioning", "scan");
+ assertThat(roles).isEmpty();
}
private Stream<String> selectRoles(String groupName) {
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest.java
new file mode 100644
index 00000000000..b894de21c6b
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v98;
+
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class UpsertSonarUsersDescriptionTest {
+
+ private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+ public static final String OLD_DESCRIPTION = "Any new users created will automatically join this group";
+ public static final String NEW_DESCRIPTION = "Every authenticated user automatically belongs to this group";
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(UpsertSonarUsersDescriptionTest.class, "schema.sql");
+
+ private final DataChange underTest = new UpsertSonarUsersDescription(db.database());
+
+ @Test
+ public void migration_populates_sonar_users_group_description() throws SQLException {
+ String uuid = insertSonarUsersGroupWithOldDescription();
+ underTest.execute();
+ assertSonarUsersGroupDescriptionIsUpsertedCorrectly(uuid);
+ }
+
+ @Test
+ public void migration_should_be_reentrant() throws SQLException {
+ String userUuid1 = insertSonarUsersGroupWithOldDescription();
+ underTest.execute();
+ // re-entrant
+ underTest.execute();
+ assertSonarUsersGroupDescriptionIsUpsertedCorrectly(userUuid1);
+ }
+
+ private void assertSonarUsersGroupDescriptionIsUpsertedCorrectly(String userUuid) {
+ String selectSql = String.format("select description from groups where uuid='%s'", userUuid);
+ assertThat(db.select(selectSql).stream().map(row -> row.get("DESCRIPTION")).collect(Collectors.toList()))
+ .containsExactlyInAnyOrder(UpsertSonarUsersDescriptionTest.NEW_DESCRIPTION);
+ }
+
+ private String insertSonarUsersGroupWithOldDescription() {
+ Map<String, Object> map = new HashMap<>();
+ String uuid = uuidFactory.create();
+ map.put("UUID", uuid);
+ map.put("NAME", "sonar-users");
+ map.put("DESCRIPTION", OLD_DESCRIPTION);
+ map.put("CREATED_AT", new Date());
+ db.executeInsert("groups", map);
+ return uuid;
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest/schema.sql
new file mode 100644
index 00000000000..6a9845ad643
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v98/UpsertSonarUsersDescriptionTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "GROUPS"(
+ "UUID" CHARACTER VARYING(40) NOT NULL,
+ "NAME" CHARACTER VARYING(500) NOT NULL,
+ "DESCRIPTION" CHARACTER VARYING(200),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+ALTER TABLE "GROUPS" ADD CONSTRAINT "PK_GROUPS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_GROUPS_NAME" ON "GROUPS"("NAME" NULLS FIRST);
diff --git a/server/sonar-web/src/main/js/apps/groups/components/List.tsx b/server/sonar-web/src/main/js/apps/groups/components/List.tsx
index a07ca958903..e3808393622 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/List.tsx
+++ b/server/sonar-web/src/main/js/apps/groups/components/List.tsx
@@ -50,6 +50,7 @@ export default function List(props: Props) {
<tr className="js-anyone" key="anyone">
<td className="width-20">
<strong className="js-group-name">{translate('groups.anyone')}</strong>
+ <span className="spacer-left badge badge-error">{translate('deprecated')}</span>
</td>
<td className="width-10" colSpan={2} />
<td className="width-40" colSpan={2}>
diff --git a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap
index 3598b1a868d..2db446a6c97 100644
--- a/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/groups/components/__tests__/__snapshots__/List-test.tsx.snap
@@ -38,6 +38,11 @@ exports[`should render 1`] = `
>
groups.anyone
</strong>
+ <span
+ className="spacer-left badge badge-error"
+ >
+ deprecated
+ </span>
</td>
<td
className="width-10"
diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx b/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx
index 0203577dab6..71bf88da055 100644
--- a/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx
+++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx
@@ -19,6 +19,7 @@
*/
import { without } from 'lodash';
import * as React from 'react';
+import { translate } from '../../../../helpers/l10n';
import GroupIcon from '../../../../components/icons/GroupIcon';
import { PermissionDefinitions, PermissionGroup } from '../../../../types/types';
import { isPermissionDefinitionGroup } from '../../utils';
@@ -35,6 +36,8 @@ interface State {
loading: string[];
}
+const ANYONE = 'Anyone';
+
export default class GroupHolder extends React.PureComponent<Props, State> {
mounted = false;
state: State = { loading: [] };
@@ -74,9 +77,14 @@ export default class GroupHolder extends React.PureComponent<Props, State> {
<div className="max-width-100">
<div className="max-width-100 text-ellipsis">
<strong>{group.name}</strong>
+ {group.name === ANYONE && (
+ <span className="spacer-left badge badge-error">{translate('deprecated')}</span>
+ )}
</div>
<div className="little-spacer-top" style={{ whiteSpace: 'normal' }}>
- {group.description}
+ {group.name === ANYONE
+ ? translate('user_groups.anyone.description')
+ : group.description}
</div>
</div>
</div>
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartup.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartup.java
new file mode 100644
index 00000000000..52f6a8b491f
--- /dev/null
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartup.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db;
+
+import java.util.List;
+import java.util.Optional;
+import org.sonar.api.Startable;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+
+/**
+ * Checks if there are any projects which have 'Anyone' group permissions at startup, after executing db migrations. If
+ * any are found, it is logged as a warning, and some example projects (up to 3) are listed. This requires to be defined
+ * in platform level 4 ({@link org.sonar.server.platform.platformlevel.PlatformLevel4}).
+ */
+public class CheckAnyonePermissionsAtStartup implements Startable {
+
+ private static final Logger LOG = Loggers.get(CheckAnyonePermissionsAtStartup.class);
+ private static final String FORCE_AUTHENTICATION_PROPERTY_NAME = "sonar.forceAuthentication";
+ private final DbClient dbClient;
+ private final Configuration config;
+
+ public CheckAnyonePermissionsAtStartup(DbClient dbClient, Configuration config) {
+ this.dbClient = dbClient;
+ this.config = config;
+ }
+
+ @Override
+ public void start() {
+ Optional<Boolean> property = config.getBoolean(FORCE_AUTHENTICATION_PROPERTY_NAME);
+ if (property.isEmpty() || Boolean.TRUE.equals(property.get())) {
+ return;
+ }
+
+ logWarningIfProjectsWithAnyonePermissionsExist();
+ }
+
+ private void logWarningIfProjectsWithAnyonePermissionsExist() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ int total = dbClient.groupPermissionDao().countProjectsWithAnyonePermissions(dbSession);
+ if (total > 0) {
+ List<String> list = dbClient.groupPermissionDao().selectProjectKeysWithAnyonePermissions(dbSession, 3);
+ LOG.warn("A total of {} public project(s) are found to have enabled 'Anyone' group permissions, including: {}. " +
+ "Make sure your project permissions are set as intended.",
+ total, String.join(", ", list));
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ // do nothing
+ }
+
+}
diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartupTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartupTest.java
new file mode 100644
index 00000000000..edd4fe9e716
--- /dev/null
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/db/CheckAnyonePermissionsAtStartupTest.java
@@ -0,0 +1,152 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db;
+
+import java.util.stream.IntStream;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.user.GroupDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CheckAnyonePermissionsAtStartupTest {
+
+ @ClassRule
+ public static LogTester logTester = new LogTester().setLevel(LoggerLevel.WARN);
+ @Rule
+ public final DbTester dbTester = DbTester.create(System2.INSTANCE);
+ private final DbClient dbClient = dbTester.getDbClient();
+ private final MapSettings settings = new MapSettings();
+ private final CheckAnyonePermissionsAtStartup underTest = new CheckAnyonePermissionsAtStartup(dbClient, settings.asConfig());
+
+ @After
+ public void tearDown() {
+ logTester.clear();
+ underTest.stop();
+ }
+
+ @Test
+ public void test_logs_present_when_exactly_3_projects_contain_anyone_permissions_and_force_authentication_false() {
+ int expectedProjectCount = 3;
+ setForceAuthentication(false);
+ execute(expectedProjectCount);
+ assertAnyonePermissionWarningInLogs(expectedProjectCount, "key-1", "key-2", "key-3");
+ }
+
+ @Test
+ public void test_logs_present_when_less_than_3_projects_contain_anyone_permissions_and_force_authentication_false() {
+ int expectedProjectCount = 1;
+ setForceAuthentication(false);
+ execute(expectedProjectCount);
+ assertAnyonePermissionWarningInLogs(expectedProjectCount, "key-1");
+ }
+
+ @Test
+ public void test_logs_present_when_more_than_3_projects_contain_anyone_permissions_and_force_authentication_false() {
+ int expectedProjectCount = 8;
+ setForceAuthentication(false);
+ execute(expectedProjectCount);
+ assertAnyonePermissionWarningInLogs(expectedProjectCount, "key-1", "key-2", "key-3");
+ }
+
+ @Test
+ public void test_logs_not_present_when_no_projects_contain_anyone_permissions_and_force_authentication_false() {
+ setForceAuthentication(false);
+ generatePublicProjectsWithGroupPermissions();
+ assertAnyonePermissionWarningNotInLogs();
+ }
+
+ @Test
+ public void test_logs_present_when_1_projects_contain_anyone_permissions_and_full_anyone_group_permission_and_force_authentication_false() {
+ // Although saved in the same table (group_roles), this should not be included in the logs as not assigned to single project.
+ dbTester.users().insertPermissionOnAnyone("perm-anyone");
+
+ int expectedProjectCount = 1;
+ setForceAuthentication(false);
+ execute(expectedProjectCount);
+ assertAnyonePermissionWarningInLogs(expectedProjectCount, "key-1");
+ }
+
+ @Test
+ public void test_logs_not_present_when_some_projects_contain_anyone_permissions_and_force_authentication_true() {
+ setForceAuthentication(true);
+ execute(3);
+ assertAnyonePermissionWarningNotInLogs();
+ }
+
+ @Test
+ public void test_logs_not_present_when_no_projects_contain_anyone_permissions_and_force_authentication_true() {
+ setForceAuthentication(true);
+ generatePublicProjectsWithGroupPermissions();
+ assertAnyonePermissionWarningNotInLogs();
+ }
+
+ @Test
+ public void test_logs_not_present_when_projects_contain_anyone_permissions_and_force_authentication_default() {
+ settings.clear();
+ execute(3);
+ assertAnyonePermissionWarningNotInLogs();
+ }
+
+ private void generatePublicProjectsWithGroupPermissions() {
+ GroupDto group = dbTester.users().insertGroup();
+ IntStream.rangeClosed(1, 3).forEach(i -> {
+ ComponentDto project = dbTester.components().insertPublicProject(p -> p.setKey("key-" + i));
+ dbTester.users().insertProjectPermissionOnGroup(group, "perm-" + i, project);
+ });
+ underTest.start();
+ }
+
+ private void execute(int projectCount) {
+ IntStream.rangeClosed(1, projectCount).forEach(i -> {
+ ComponentDto project = dbTester.components().insertPublicProject(p -> p.setKey("key-" + i));
+ dbTester.users().insertProjectPermissionOnAnyone("perm-" + i, project);
+ });
+ underTest.start();
+ }
+
+ private void setForceAuthentication(Boolean isForceAuthentication) {
+ settings.setProperty("sonar.forceAuthentication", isForceAuthentication.toString());
+ }
+
+ private void assertAnyonePermissionWarningNotInLogs() {
+ boolean noneMatch = logTester.logs().stream()
+ .noneMatch(s -> s.matches(".*A total of [0-9]+ public project\\(s\\) are found to have enabled 'Anyone' group permissions, including: %s. " +
+ "Make sure your project permissions are set as intended.*"));
+ assertThat(noneMatch).isTrue();
+ }
+
+ private void assertAnyonePermissionWarningInLogs(int expectedProjectCountString, String... expectedListedProjects) {
+ String expected = String.format("A total of %d public project(s) are found to have enabled 'Anyone' group permissions, including: %s. " +
+ "Make sure your project permissions are set as intended.",
+ expectedProjectCountString, String.join(", ", expectedListedProjects));
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains(expected);
+ }
+
+}
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/groups-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/groups-example.json
index 9a67c7fa65f..5e69f5b688f 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/groups-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/groups-example.json
@@ -18,7 +18,7 @@
{
"id": "AU-Tpxb--iU5OvuD2FLz",
"name": "sonar-users",
- "description": "Any new users created will automatically join this group",
+ "description": "Every authenticated user automatically belongs to this group",
"permissions": []
}
]
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/template/template_groups-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/template/template_groups-example.json
index 7a8ead45905..ee39ebfac02 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/template/template_groups-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/permission/ws/template/template_groups-example.json
@@ -21,7 +21,7 @@
},
{
"name": "sonar-users",
- "description": "Any new users created will automatically join this group",
+ "description": "Every authenticated user automatically belongs to this group",
"permissions": [
"issueadmin"
]
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/TemplateGroupsActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/TemplateGroupsActionTest.java
index 3b74ec90074..44508533191 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/TemplateGroupsActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/ws/template/TemplateGroupsActionTest.java
@@ -85,7 +85,7 @@ public class TemplateGroupsActionTest extends BasePermissionWsTest<TemplateGroup
@Test
public void template_groups_of_json_example() {
GroupDto adminGroup = insertGroup("sonar-administrators", "System administrators");
- GroupDto userGroup = insertGroup("sonar-users", "Any new users created will automatically join this group");
+ GroupDto userGroup = insertGroup("sonar-users", "Every authenticated user automatically belongs to this group");
PermissionTemplateDto template = addTemplate();
addGroupToTemplate(newPermissionTemplateGroup(ISSUE_ADMIN, template.getUuid(), adminGroup.getUuid()), template.getName());
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 3f3c27434fe..a57f28666b7 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -155,6 +155,7 @@ import org.sonar.server.platform.ClusterVerification;
import org.sonar.server.platform.PersistentSettings;
import org.sonar.server.platform.SystemInfoWriterModule;
import org.sonar.server.platform.WebCoreExtensionsInstaller;
+import org.sonar.server.platform.db.CheckAnyonePermissionsAtStartup;
import org.sonar.server.platform.web.SonarLintConnectionFilter;
import org.sonar.server.platform.web.WebServiceFilter;
import org.sonar.server.platform.web.WebServiceReroutingFilter;
@@ -412,6 +413,7 @@ public class PlatformLevel4 extends PlatformLevel {
PermissionUpdater.class,
UserPermissionChanger.class,
GroupPermissionChanger.class,
+ CheckAnyonePermissionsAtStartup.class,
// components
new BranchWsModule(),
@@ -631,7 +633,6 @@ public class PlatformLevel4 extends PlatformLevel {
MainCollector.class,
PluginsRiskConsentFilter.class
-
);
// system info
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 411dd234743..a0405c19920 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -4288,7 +4288,7 @@ users.change_admin_password.form.continue_to_app=Continue to SonarQube
#------------------------------------------------------------------------------
user_groups.page=Groups
user_groups.page.description=Create and administer groups of users.
-user_groups.anyone.description=Anybody (authenticated or not) who browses the application belongs to this group
+user_groups.anyone.description=Anybody (authenticated or not) who browses the application belongs to this group. If Force user authentication is disabled, any permissions assigned to this group will apply to non-authenticated users.
groups.delete_group=Delete Group
groups.delete_group.confirmation=Are you sure you want to delete "{0}"?
groups.create_group=Create Group