diff options
author | Benoit <benoit.gianinetti@sonarsource.com> | 2019-07-12 14:06:47 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-07-12 20:21:16 +0200 |
commit | 7f1afd8ce4723dad04762837efec3b4b2525dff5 (patch) | |
tree | 868fce46d90be48623e237c195a64e9aff091696 /server/sonar-db-dao | |
parent | c2d9ced3637a5aa08422427e6435aaecaf659feb (diff) | |
download | sonarqube-7f1afd8ce4723dad04762837efec3b4b2525dff5.tar.gz sonarqube-7f1afd8ce4723dad04762837efec3b4b2525dff5.zip |
MMF-769 User can close their account (#1861)
Diffstat (limited to 'server/sonar-db-dao')
12 files changed, 251 insertions, 242 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDto.java index f82bd6588a8..33a703a0be9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDto.java @@ -73,10 +73,6 @@ public class OrganizationDto { private Subscription subscription; - /** - * Flag indicated whether being root is required to be able to delete this organization. - */ - private boolean guarded = false; private Integer defaultGroupId; private String defaultQualityGateUuid; private long createdAt; @@ -139,15 +135,6 @@ public class OrganizationDto { return this; } - public boolean isGuarded() { - return guarded; - } - - public OrganizationDto setGuarded(boolean guarded) { - this.guarded = guarded; - return this; - } - @CheckForNull public Integer getDefaultGroupId() { return defaultGroupId; @@ -220,7 +207,6 @@ public class OrganizationDto { ", description='" + description + '\'' + ", url='" + url + '\'' + ", avatarUrl='" + avatarUrl + '\'' + - ", guarded=" + guarded + ", defaultQualityGateUuid=" + defaultQualityGateUuid + ", subscription=" + subscription + ", createdAt=" + createdAt + diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationHelper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationHelper.java new file mode 100644 index 00000000000..ef304882a3a --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationHelper.java @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.db.organization; + +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.permission.OrganizationPermission; + +public class OrganizationHelper { + + private static final String ADMIN_PERMISSION = OrganizationPermission.ADMINISTER.getKey(); + + private final DbClient dbClient; + + public OrganizationHelper(DbClient dbClient) { + this.dbClient = dbClient; + } + + public List<OrganizationDto> selectOrganizationsWithLastAdmin(DbSession dbSession, int userId) { + return dbClient.organizationDao().selectByPermission(dbSession, userId, ADMIN_PERMISSION).stream() + .filter(org -> isLastAdmin(dbSession, org, userId)) + .collect(Collectors.toList()); + } + + private boolean isLastAdmin(DbSession dbSession, OrganizationDto org, int userId) { + return dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingUser(dbSession, org.getUuid(), ADMIN_PERMISSION, userId) == 0; + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java index da63b59d512..899bcba33cb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java @@ -32,27 +32,15 @@ public class OrganizationQuery { private final Set<String> keys; @Nullable private final Integer userId; - private final boolean onlyTeam; - private final boolean onlyPersonal; private final boolean withAnalyses; - private final boolean withoutProjects; @Nullable private final Long analyzedAfter; private OrganizationQuery(Builder builder) { this.keys = builder.keys; this.userId = builder.member; - this.onlyPersonal = builder.onlyPersonal; - this.onlyTeam = builder.onlyTeam; - if (this.onlyPersonal && this.onlyTeam) { - throw new IllegalArgumentException("Only one of onlyPersonal and onlyTeam can be true"); - } this.withAnalyses = builder.withAnalyses; this.analyzedAfter = builder.analyzedAfter; - this.withoutProjects = builder.withoutProjects; - if ((this.withAnalyses || this.analyzedAfter != null) && this.withoutProjects) { - throw new IllegalArgumentException("withoutProjects cannot be used together with withAnalyses or analyzedAfter"); - } } @CheckForNull @@ -65,14 +53,6 @@ public class OrganizationQuery { return userId; } - public boolean isOnlyTeam() { - return onlyTeam; - } - - public boolean isOnlyPersonal() { - return onlyPersonal; - } - public boolean isWithAnalyses() { return withAnalyses; } @@ -82,10 +62,6 @@ public class OrganizationQuery { return analyzedAfter; } - public boolean isWithoutProjects() { - return withoutProjects; - } - public static OrganizationQuery returnAll() { return NO_FILTER; } @@ -98,10 +74,7 @@ public class OrganizationQuery { private Set<String> keys; @Nullable private Integer member; - private boolean onlyTeam = false; - private boolean onlyPersonal = false; private boolean withAnalyses = false; - private boolean withoutProjects = false; @Nullable private Long analyzedAfter; @@ -123,16 +96,6 @@ public class OrganizationQuery { return this; } - public Builder setOnlyTeam() { - this.onlyTeam = true; - return this; - } - - public Builder setOnlyPersonal() { - this.onlyPersonal = true; - return this; - } - public Builder setWithAnalyses() { this.withAnalyses = true; return this; @@ -143,11 +106,6 @@ public class OrganizationQuery { return this; } - public Builder setWithoutProjects() { - this.withoutProjects = true; - return this; - } - public OrganizationQuery build() { return new OrganizationQuery(this); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index 7fe0e24e73d..744249a2ce6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -135,6 +135,10 @@ public class UserDao implements Dao { mapper(dbSession).deactivateUser(user.getLogin(), system2.now()); } + public void deactivateSonarCloudUser(DbSession dbSession, UserDto user) { + mapper(dbSession).deactivateSonarCloudUser(user.getLogin(), system2.now()); + } + public void cleanHomepage(DbSession dbSession, OrganizationDto organization) { mapper(dbSession).clearHomepages("ORGANIZATION", organization.getUuid(), system2.now()); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java index 2110735f660..c8a414f934a 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDto.java @@ -55,7 +55,6 @@ public class UserDto { private boolean local = true; private boolean root = false; private boolean onboarded = false; - private String organizationUuid; /** * Date of the last time the user has accessed to the server. @@ -273,16 +272,6 @@ public class UserDto { } @CheckForNull - public String getOrganizationUuid() { - return organizationUuid; - } - - public UserDto setOrganizationUuid(@Nullable String organizationUuid) { - this.organizationUuid = organizationUuid; - return this; - } - - @CheckForNull public Long getLastConnectionDate() { return lastConnectionDate; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java index 8f1a77c7b86..8c83ba012c3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java @@ -82,6 +82,8 @@ public interface UserMapper { void deactivateUser(@Param("login") String login, @Param("now") long now); + void deactivateSonarCloudUser(@Param("login") String login, @Param("now") long now); + void clearHomepages(@Param("homepageType") String type, @Param("homepageParameter") String value, @Param("now") long now); void clearHomepage(@Param("login") String login, @Param("now") long now); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml index a806a01d78a..b23b1b46960 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml @@ -10,7 +10,6 @@ org.default_quality_gate_uuid as "defaultQualityGateUuid", org.url as "url", org.avatar_url as "avatarUrl", - org.guarded as "guarded", org.subscription as "subscription", org.created_at as "createdAt", org.updated_at as "updatedAt" @@ -121,22 +120,6 @@ #{key, jdbcType=VARCHAR} </foreach> </if> - <if test="query.onlyTeam"> - and not exists( - select 1 - from users u - where u.organization_uuid = org.uuid - and u.active = ${_true} - ) - </if> - <if test="query.onlyPersonal"> - and exists( - select 1 - from users u - where u.organization_uuid = org.uuid - and u.active = ${_true} - ) - </if> <if test="query.withAnalyses"> and exists( select 1 @@ -147,14 +130,6 @@ and s.islast = ${_true} ) </if> - <if test="query.withoutProjects"> - and not exists( - select 1 - from projects p - where p.organization_uuid = org.uuid - and p.enabled = ${_true} - ) - </if> <if test="query.analyzedAfter != null"> and exists( select 1 @@ -246,7 +221,6 @@ description, url, avatar_url, - guarded, new_project_private, default_quality_gate_uuid, subscription, @@ -261,7 +235,6 @@ #{organization.description, jdbcType=VARCHAR}, #{organization.url, jdbcType=VARCHAR}, #{organization.avatarUrl, jdbcType=VARCHAR}, - #{organization.guarded, jdbcType=BOOLEAN}, #{newProjectPrivate, jdbcType=BOOLEAN}, #{organization.defaultQualityGateUuid, jdbcType=VARCHAR}, #{organization.subscription, jdbcType=VARCHAR}, diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml index bf0ee3a5d81..d6c6c9b059f 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml @@ -22,7 +22,6 @@ u.onboarded as "onboarded", u.homepage_type as "homepageType", u.homepage_parameter as "homepageParameter", - u.organization_uuid as organizationUuid, u.last_connection_date as "lastConnectionDate", u.created_at as "createdAt", u.updated_at as "updatedAt" @@ -173,8 +172,7 @@ and u.login <> #{login} </select> - <update id="deactivateUser" parameterType="map"> - update users set + <sql id="deactivateUserUpdatedFields"> active = ${_false}, email = null, scm_accounts = null, @@ -182,6 +180,19 @@ crypted_password = null, last_connection_date = null, updated_at = #{now, jdbcType=BIGINT} + </sql> + + <update id="deactivateUser" parameterType="map"> + update users set + <include refid="deactivateUserUpdatedFields"/> + where + login = #{login, jdbcType=VARCHAR} + </update> + + <update id="deactivateSonarCloudUser" parameterType="map"> + update users set + name = null, + <include refid="deactivateUserUpdatedFields"/> where login = #{login, jdbcType=VARCHAR} </update> @@ -233,7 +244,6 @@ onboarded, homepage_type, homepage_parameter, - organization_uuid, created_at, updated_at ) values ( @@ -254,7 +264,6 @@ #{user.onboarded,jdbcType=BOOLEAN}, #{user.homepageType,jdbcType=VARCHAR}, #{user.homepageParameter,jdbcType=VARCHAR}, - #{user.organizationUuid,jdbcType=VARCHAR}, #{user.createdAt,jdbcType=BIGINT}, #{user.updatedAt,jdbcType=BIGINT} ) @@ -277,7 +286,6 @@ hash_method = #{user.hashMethod, jdbcType=VARCHAR}, homepage_type = #{user.homepageType, jdbcType=VARCHAR}, homepage_parameter = #{user.homepageParameter, jdbcType=VARCHAR}, - organization_uuid = #{user.organizationUuid, jdbcType=VARCHAR}, last_connection_date = #{user.lastConnectionDate,jdbcType=BIGINT}, updated_at = #{user.updatedAt,jdbcType=BIGINT} where diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java index ef389bc5223..debde182ede 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java @@ -50,8 +50,6 @@ import org.sonar.db.Pagination; import org.sonar.db.alm.ALM; import org.sonar.db.alm.AlmAppInstallDto; import org.sonar.db.component.ComponentDto; -import org.sonar.db.dialect.Dialect; -import org.sonar.db.dialect.Oracle; import org.sonar.db.metric.MetricDto; import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.db.user.GroupDto; @@ -86,7 +84,6 @@ public class OrganizationDaoTest { .setDescription("the description 1") .setUrl("the url 1") .setAvatarUrl("the avatar url 1") - .setGuarded(false) .setSubscription(FREE) .setDefaultQualityGateUuid("1"); private static final OrganizationDto ORGANIZATION_DTO_2 = new OrganizationDto() @@ -96,7 +93,6 @@ public class OrganizationDaoTest { .setDescription("the description 2") .setUrl("the url 2") .setAvatarUrl("the avatar url 2") - .setGuarded(true) .setSubscription(FREE) .setDefaultQualityGateUuid("1"); private static final String PERMISSION_1 = "foo"; @@ -148,7 +144,6 @@ public class OrganizationDaoTest { assertThat(row.get("avatarUrl")).isEqualTo(organization.getAvatarUrl()); assertThat(row.get("createdAt")).isEqualTo(organization.getCreatedAt()); assertThat(row.get("updatedAt")).isEqualTo(organization.getUpdatedAt()); - assertThat(row.get("guarded")).isEqualTo(toBool(organization.isGuarded())); assertThat(row.get("subscription")).isEqualTo(organization.getSubscription().name()); assertThat(row.get("defaultTemplate")).isNull(); assertThat(row.get("projectDefaultTemplate")).isNull(); @@ -156,14 +151,6 @@ public class OrganizationDaoTest { } @Test - public void insert_persists_boolean_property_guarded_of_OrganizationDto() { - insertOrganization(ORGANIZATION_DTO_2); - - Map<String, Object> row = selectSingleRow(); - assertThat(row.get("guarded")).isEqualTo(toBool(ORGANIZATION_DTO_2.isGuarded())); - } - - @Test public void description_url_avatarUrl_and_userId_are_optional() { when(system2.now()).thenReturn(SOME_DATE); insertOrganization(copyOf(ORGANIZATION_DTO_1).setDescription(null).setUrl(null).setAvatarUrl(null)); @@ -175,7 +162,6 @@ public class OrganizationDaoTest { assertThat(row.get("description")).isNull(); assertThat(row.get("url")).isNull(); assertThat(row.get("avatarUrl")).isNull(); - assertThat(row.get("guarded")).isEqualTo(toBool(ORGANIZATION_DTO_1.isGuarded())); assertThat(row.get("userId")).isNull(); assertThat(row.get("createdAt")).isEqualTo(SOME_DATE); assertThat(row.get("updatedAt")).isEqualTo(SOME_DATE); @@ -184,14 +170,6 @@ public class OrganizationDaoTest { assertThat(row.get("viewDefaultTemplate")).isNull(); } - private Object toBool(boolean guarded) { - Dialect dialect = db.database().getDialect(); - if (dialect.getId().equals(Oracle.ID)) { - return guarded ? 1L : 0L; - } - return guarded; - } - @Test public void insert_fails_if_row_with_uuid_already_exists() { insertOrganization(ORGANIZATION_DTO_1); @@ -585,20 +563,6 @@ public class OrganizationDaoTest { } @Test - public void selectByQuery_filter_on_type() { - OrganizationDto personalOrg1 = db.organizations().insert(); - db.users().insertUser(u -> u.setOrganizationUuid(personalOrg1.getUuid())); - OrganizationDto personalOrg2 = db.organizations().insert(); - db.users().insertUser(u -> u.setOrganizationUuid(personalOrg2.getUuid())); - OrganizationDto teamOrg1 = db.organizations().insert(); - - assertThat(selectUuidsByQuery(q -> q.setOnlyPersonal(), forPage(1).andSize(100))) - .containsExactlyInAnyOrder(personalOrg1.getUuid(), personalOrg2.getUuid()); - assertThat(selectUuidsByQuery(q -> q.setOnlyTeam(), forPage(1).andSize(100))) - .containsExactlyInAnyOrder(teamOrg1.getUuid()); - } - - @Test public void selectByQuery_filter_on_withAnalyses() { assertThat(selectUuidsByQuery(q -> q.setWithAnalyses(), forPage(1).andSize(100))) .isEmpty(); @@ -630,25 +594,6 @@ public class OrganizationDaoTest { } @Test - public void selectByQuery_filter_on_withoutProjects() { - assertThat(selectUuidsByQuery(q -> q.setWithoutProjects(), forPage(1).andSize(100))) - .isEmpty(); - - // has projects - OrganizationDto orgWithProjects = db.organizations().insert(); - db.components().insertPrivateProject(orgWithProjects); - db.components().insertPrivateProject(orgWithProjects, p -> p.setEnabled(false)); - // has no projects - OrganizationDto orgWithoutProjects = db.organizations().insert(); - // has only disabled projects - OrganizationDto orgWithOnlyDisabledProjects = db.organizations().insert(); - db.components().insertPrivateProject(orgWithOnlyDisabledProjects, p -> p.setEnabled(false)); - - assertThat(selectUuidsByQuery(q -> q.setWithoutProjects(), forPage(1).andSize(100))) - .containsExactlyInAnyOrder(orgWithoutProjects.getUuid(), orgWithOnlyDisabledProjects.getUuid()); - } - - @Test public void getDefaultTemplates_returns_empty_when_table_is_empty() { assertThat(underTest.getDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid())).isEmpty(); } @@ -1155,7 +1100,6 @@ public class OrganizationDaoTest { " default_perm_template_app," + " default_perm_template_port," + " new_project_private," + - " guarded," + " default_quality_gate_uuid," + " subscription," + " created_at," + @@ -1173,7 +1117,6 @@ public class OrganizationDaoTest { " ?," + " ?," + " ?," + - " ?," + " ?" + " )")) { preparedStatement.setString(1, organizationUuid); @@ -1183,11 +1126,10 @@ public class OrganizationDaoTest { preparedStatement.setString(5, view); preparedStatement.setString(6, view); preparedStatement.setBoolean(7, false); - preparedStatement.setBoolean(8, false); - preparedStatement.setString(9, "1"); - preparedStatement.setString(10, FREE.name()); - preparedStatement.setLong(11, 1000L); - preparedStatement.setLong(12, 2000L); + preparedStatement.setString(8, "1"); + preparedStatement.setString(9, FREE.name()); + preparedStatement.setLong(10, 1000L); + preparedStatement.setLong(11, 2000L); preparedStatement.execute(); } catch (SQLException e) { throw new RuntimeException("dirty insert failed", e); @@ -1210,7 +1152,6 @@ public class OrganizationDaoTest { assertThat(dto.getName()).isEqualTo(ORGANIZATION_DTO_1.getName()); assertThat(dto.getDescription()).isEqualTo(ORGANIZATION_DTO_1.getDescription()); assertThat(dto.getUrl()).isEqualTo(ORGANIZATION_DTO_1.getUrl()); - assertThat(dto.isGuarded()).isEqualTo(ORGANIZATION_DTO_1.isGuarded()); assertThat(dto.getAvatarUrl()).isEqualTo(ORGANIZATION_DTO_1.getAvatarUrl()); assertThat(dto.getCreatedAt()).isEqualTo(ORGANIZATION_DTO_1.getCreatedAt()); assertThat(dto.getUpdatedAt()).isEqualTo(ORGANIZATION_DTO_1.getUpdatedAt()); @@ -1222,7 +1163,6 @@ public class OrganizationDaoTest { assertThat(dto.getName()).isEqualTo(expected.getName()); assertThat(dto.getDescription()).isEqualTo(expected.getDescription()); assertThat(dto.getUrl()).isEqualTo(expected.getUrl()); - assertThat(dto.isGuarded()).isEqualTo(expected.isGuarded()); assertThat(dto.getAvatarUrl()).isEqualTo(expected.getAvatarUrl()); assertThat(dto.getSubscription()).isEqualTo(expected.getSubscription()); assertThat(dto.getCreatedAt()).isEqualTo(expected.getCreatedAt()); @@ -1232,7 +1172,6 @@ public class OrganizationDaoTest { private Map<String, Object> selectSingleRow() { List<Map<String, Object>> rows = db.select("select" + " uuid as \"uuid\", kee as \"key\", name as \"name\", description as \"description\", url as \"url\", avatar_url as \"avatarUrl\"," + - " guarded as \"guarded\"," + " subscription as \"subscription\"," + " created_at as \"createdAt\", updated_at as \"updatedAt\"," + " default_perm_template_project as \"projectDefaultPermTemplate\"," + diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationHelperTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationHelperTest.java new file mode 100644 index 00000000000..bc5d738366c --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationHelperTest.java @@ -0,0 +1,141 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.db.organization; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.List; +import java.util.Random; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.sonar.api.utils.System2; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.user.GroupDto; +import org.sonar.db.user.UserDto; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; + +@RunWith(DataProviderRunner.class) +public class OrganizationHelperTest { + + private static final Random RANDOM = new Random(); + + @Rule + public DbTester db = DbTester.create(mock(System2.class)).setDisableDefaultOrganization(true); + public DbSession dbSession = db.getSession(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private final OrganizationHelper underTest = new OrganizationHelper(db.getDbClient()); + + @Test + public void returns_empty_list_when_user_is_not_admin_of_any_orgs() { + UserDto user1 = db.users().insertUser(); + + OrganizationDto org1 = db.organizations().insert(); + GroupDto group1 = db.users().insertGroup(org1); + db.users().insertMember(group1, user1); + + assertThat(underTest.selectOrganizationsWithLastAdmin(dbSession, user1.getId())).isEmpty(); + } + + @Test + public void returns_orgs_where_user_is_last_admin() { + UserDto user1 = db.users().insertUser(); + UserDto user2 = db.users().insertUser(); + + OrganizationDto org1 = db.organizations().insert(); + OrganizationDto org2 = db.organizations().insert(); + + setAsDirectOrIndirectAdmin(user1, org1); + setAsDirectOrIndirectAdmin(user2, org1); + setAsDirectOrIndirectAdmin(user1, org2); + + assertThat(underTest.selectOrganizationsWithLastAdmin(dbSession, user1.getId())) + .extracting(OrganizationDto::getKey) + .containsExactly(org2.getKey()); + } + + @Test + @UseDataProvider("adminUserCombinationsAndExpectedOrgKeys") + public void returns_correct_orgs_for_interesting_combinations_of_last_admin_or_not( + boolean user2IsAdminOfOrg1, boolean user1IsAdminOfOrg2, boolean user2IsAdminOfOrg2, List<String> expectedOrgKeys) { + UserDto user1 = db.users().insertUser(); + UserDto user2 = db.users().insertUser(); + + OrganizationDto org1 = db.organizations().insert(o -> o.setKey("org1")); + OrganizationDto org2 = db.organizations().insert(o -> o.setKey("org2")); + + setAsDirectOrIndirectAdmin(user1, org1); + if (user2IsAdminOfOrg1) { + setAsDirectOrIndirectAdmin(user2, org1); + } + if (user1IsAdminOfOrg2) { + setAsDirectOrIndirectAdmin(user1, org2); + } + if (user2IsAdminOfOrg2) { + setAsDirectOrIndirectAdmin(user2, org2); + } + + assertThat(underTest.selectOrganizationsWithLastAdmin(dbSession, user1.getId())) + .extracting(OrganizationDto::getKey) + .containsExactlyInAnyOrderElementsOf(expectedOrgKeys); + } + + @DataProvider + public static Object[][] adminUserCombinationsAndExpectedOrgKeys() { + return new Object[][] { + // note: user1 is always admin of org1 + // param 1: user2 is admin of org1 + // param 2: user1 is admin of org2 + // param 3: user2 is admin of org2 + // param 4: list of orgs preventing user1 to delete + {true, true, true, emptyList()}, + {true, true, false, singletonList("org2")}, + {true, false, true, emptyList()}, + {true, false, false, emptyList()}, + {false, true, true, singletonList("org1")}, + {false, true, false, asList("org1", "org2")}, + {false, false, true, singletonList("org1")}, + {false, false, false, singletonList("org1")}, + }; + } + + private void setAsDirectOrIndirectAdmin(UserDto user, OrganizationDto organization) { + boolean useDirectAdmin = RANDOM.nextBoolean(); + if (useDirectAdmin) { + db.users().insertPermissionOnUser(organization, user, ADMINISTER); + } else { + GroupDto group = db.users().insertGroup(organization); + db.users().insertPermissionOnGroup(group, ADMINISTER); + db.users().insertMember(group, user); + } + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationQueryTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationQueryTest.java deleted file mode 100644 index 74f60141463..00000000000 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationQueryTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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.db.organization; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -public class OrganizationQueryTest { - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Test - public void throws_IAE_when_both_onlyPersonal_and_onlyTeam_are_set() { - expectedException.expect(IllegalArgumentException.class); - OrganizationQuery.newOrganizationQueryBuilder() - .setOnlyPersonal() - .setOnlyTeam() - .build(); - } - - @Test - public void throws_IAE_when_withoutProjects_is_used_together_with_withAnalyses() { - expectedException.expect(IllegalArgumentException.class); - OrganizationQuery.newOrganizationQueryBuilder() - .setWithoutProjects() - .setWithAnalyses() - .build(); - } - - @Test - public void throws_IAE_when_withoutProjects_is_used_together_with_analyzedAfter() { - expectedException.expect(IllegalArgumentException.class); - OrganizationQuery.newOrganizationQueryBuilder() - .setWithoutProjects() - .setAnalyzedAfter(1) - .build(); - } - - @Test - public void throws_IAE_when_withoutProjects_is_used_together_with_both_withAnalyses_and_analyzedAfter() { - expectedException.expect(IllegalArgumentException.class); - OrganizationQuery.newOrganizationQueryBuilder() - .setWithoutProjects() - .setWithAnalyses() - .setAnalyzedAfter(1) - .build(); - } -} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java index e739b57e69a..1f02bd04a34 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java @@ -19,12 +19,15 @@ */ package org.sonar.db.user; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.sonar.api.user.UserQuery; import org.sonar.api.utils.DateUtils; import org.sonar.api.impl.utils.TestSystem2; @@ -44,6 +47,7 @@ import static org.assertj.core.groups.Tuple.tuple; import static org.sonar.db.user.GroupTesting.newGroupDto; import static org.sonar.db.user.UserTesting.newUserDto; +@RunWith(DataProviderRunner.class) public class UserDaoTest { private static final long NOW = 1_500_000_000_000L; @@ -336,7 +340,6 @@ public class UserDaoTest { .setLocal(true) .setHomepageType("project") .setHomepageParameter("OB1") - .setOrganizationUuid("ORG_UUID") .setCreatedAt(date) .setUpdatedAt(date); underTest.insert(db.getSession(), userDto); @@ -361,7 +364,6 @@ public class UserDaoTest { assertThat(user.isRoot()).isFalse(); assertThat(user.getHomepageType()).isEqualTo("project"); assertThat(user.getHomepageParameter()).isEqualTo("OB1"); - assertThat(user.getOrganizationUuid()).isEqualTo("ORG_UUID"); } @Test @@ -383,8 +385,7 @@ public class UserDaoTest { .setEmail("jo@hn.com") .setActive(true) .setLocal(true) - .setOnboarded(false) - .setOrganizationUuid("OLD_ORG_UUID")); + .setOnboarded(false)); underTest.update(db.getSession(), newUserDto() .setUuid(user.getUuid()) @@ -403,7 +404,6 @@ public class UserDaoTest { .setLocal(false) .setHomepageType("project") .setHomepageParameter("OB1") - .setOrganizationUuid("ORG_UUID") .setLastConnectionDate(10_000_000_000L)); UserDto reloaded = underTest.selectByUuid(db.getSession(), user.getUuid()); @@ -425,7 +425,6 @@ public class UserDaoTest { assertThat(reloaded.isRoot()).isFalse(); assertThat(reloaded.getHomepageType()).isEqualTo("project"); assertThat(reloaded.getHomepageParameter()).isEqualTo("OB1"); - assertThat(reloaded.getOrganizationUuid()).isEqualTo("ORG_UUID"); assertThat(reloaded.getLastConnectionDate()).isEqualTo(10_000_000_000L); } @@ -441,10 +440,40 @@ public class UserDaoTest { UserDto userReloaded = underTest.selectUserById(session, user.getId()); assertThat(userReloaded.isActive()).isFalse(); - assertThat(userReloaded.getLogin()).isNotNull(); - assertThat(userReloaded.getExternalId()).isNotNull(); - assertThat(userReloaded.getExternalLogin()).isNotNull(); - assertThat(userReloaded.getExternalIdentityProvider()).isNotNull(); + assertThat(userReloaded.getName()).isEqualTo(user.getName()); + assertThat(userReloaded.getLogin()).isEqualTo(user.getLogin()); + assertThat(userReloaded.getExternalId()).isEqualTo(user.getExternalId()); + assertThat(userReloaded.getExternalLogin()).isEqualTo(user.getExternalLogin()); + assertThat(userReloaded.getExternalIdentityProvider()).isEqualTo(user.getExternalIdentityProvider()); + assertThat(userReloaded.getEmail()).isNull(); + assertThat(userReloaded.getScmAccounts()).isNull(); + assertThat(userReloaded.getSalt()).isNull(); + assertThat(userReloaded.getCryptedPassword()).isNull(); + assertThat(userReloaded.isRoot()).isFalse(); + assertThat(userReloaded.getUpdatedAt()).isEqualTo(NOW); + assertThat(userReloaded.getHomepageType()).isNull(); + assertThat(userReloaded.getHomepageParameter()).isNull(); + assertThat(userReloaded.getLastConnectionDate()).isNull(); + assertThat(underTest.selectUserById(session, otherUser.getId())).isNotNull(); + } + + @Test + public void deactivate_sonarcloud_user() { + UserDto user = insertActiveUser(); + insertUserGroup(user); + UserDto otherUser = insertActiveUser(); + underTest.update(db.getSession(), user.setLastConnectionDate(10_000_000_000L)); + session.commit(); + + underTest.deactivateSonarCloudUser(session, user); + + UserDto userReloaded = underTest.selectUserById(session, user.getId()); + assertThat(userReloaded.isActive()).isFalse(); + assertThat(userReloaded.getName()).isNull(); + assertThat(userReloaded.getLogin()).isEqualTo(user.getLogin()); + assertThat(userReloaded.getExternalId()).isEqualTo(user.getExternalId()); + assertThat(userReloaded.getExternalLogin()).isEqualTo(user.getExternalLogin()); + assertThat(userReloaded.getExternalIdentityProvider()).isEqualTo(user.getExternalIdentityProvider()); assertThat(userReloaded.getEmail()).isNull(); assertThat(userReloaded.getScmAccounts()).isNull(); assertThat(userReloaded.getSalt()).isNull(); |