From 812ba30a2eb3f13768b75dd40c40a0fcccad0425 Mon Sep 17 00:00:00 2001 From: Belen Pruvost Date: Wed, 3 Nov 2021 16:32:41 +0100 Subject: [PATCH] SONAR-15603 - Quality Profile permission delegation logs --- .../org/sonar/db/audit/AuditPersister.java | 4 + .../sonar/db/audit/NoOpAuditPersister.java | 10 ++ .../audit/model/AbstractEditorNewValue.java | 14 ++ .../db/audit/model/GroupEditorNewValue.java | 23 ++++ .../db/audit/model/UserEditorNewValue.java | 46 +++++-- .../sonar/db/qualityprofile/QProfileDto.java | 25 +++- .../qualityprofile/QProfileEditGroupsDao.java | 34 ++++- .../QProfileEditGroupsMapper.java | 6 +- .../qualityprofile/QProfileEditUsersDao.java | 34 ++++- .../QProfileEditUsersMapper.java | 6 +- .../QProfileEditGroupsDaoTest.java | 72 ++++++++++- .../QProfileEditUsersDaoTest.java | 120 +++++++++++++----- .../QualityProfileDbTester.java | 21 +-- .../qualityprofile/ws/AddGroupAction.java | 4 +- .../qualityprofile/ws/AddUserAction.java | 3 +- 15 files changed, 348 insertions(+), 74 deletions(-) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java index 5cb1ef06044..60987dbf424 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java @@ -101,6 +101,10 @@ public interface AuditPersister { void deleteQualityGateEditor(DbSession dbSession, AbstractEditorNewValue newValue); + void addQualityProfileEditor(DbSession dbSession, AbstractEditorNewValue newValue); + + void deleteQualityProfileEditor(DbSession dbSession, AbstractEditorNewValue newValue); + void addCharacteristicToPermissionTemplate(DbSession dbSession, PermissionTemplateNewValue newValue); void updateCharacteristicInPermissionTemplate(DbSession dbSession, PermissionTemplateNewValue newValue); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java index 52a44b4a6ee..2de7874e9f8 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/NoOpAuditPersister.java @@ -188,6 +188,16 @@ public class NoOpAuditPersister implements AuditPersister { // no op } + @Override + public void addQualityProfileEditor(DbSession dbSession, AbstractEditorNewValue newValue) { + // no op + } + + @Override + public void deleteQualityProfileEditor(DbSession dbSession, AbstractEditorNewValue newValue) { + // no op + } + @Override public void addCharacteristicToPermissionTemplate(DbSession dbSession, PermissionTemplateNewValue newValue) { // no op diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/AbstractEditorNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/AbstractEditorNewValue.java index 0146375640a..7969d398732 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/AbstractEditorNewValue.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/AbstractEditorNewValue.java @@ -27,6 +27,10 @@ public abstract class AbstractEditorNewValue extends NewValue { protected String qualityGateUuid; @Nullable protected String qualityGateName; + @Nullable + protected String qualityProfileUuid; + @Nullable + protected String qualityProfileName; @CheckForNull public String getQualityGateUuid() { @@ -37,4 +41,14 @@ public abstract class AbstractEditorNewValue extends NewValue { public String getQualityGateName() { return this.qualityGateName; } + + @CheckForNull + public String getQualityProfileUuid() { + return this.qualityProfileUuid; + } + + @CheckForNull + public String getQualityProfileName() { + return this.qualityProfileName; + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/GroupEditorNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/GroupEditorNewValue.java index d846220e2f2..21436bb81cf 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/GroupEditorNewValue.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/GroupEditorNewValue.java @@ -23,6 +23,8 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.db.qualitygate.QualityGateGroupPermissionsDto; +import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.db.qualityprofile.QProfileEditGroupsDto; import org.sonar.db.user.GroupDto; public class GroupEditorNewValue extends AbstractEditorNewValue { @@ -55,6 +57,25 @@ public class GroupEditorNewValue extends AbstractEditorNewValue { this.groupName = groupDto.getName(); } + public GroupEditorNewValue(QProfileEditGroupsDto qProfileEditGroupsDto, String qualityProfileName, String groupName) { + this.qualityProfileUuid = qProfileEditGroupsDto.getQProfileUuid(); + this.qualityProfileName = qualityProfileName; + this.groupUuid = qProfileEditGroupsDto.getGroupUuid(); + this.groupName = groupName; + } + + public GroupEditorNewValue(QProfileDto qualityProfileDto, GroupDto groupDto) { + this.qualityProfileUuid = qualityProfileDto.getKee(); + this.qualityProfileName = qualityProfileDto.getName(); + this.groupUuid = groupDto.getUuid(); + this.groupName = groupDto.getName(); + } + + public GroupEditorNewValue(QProfileDto qualityProfileDto) { + this.qualityProfileUuid = qualityProfileDto.getKee(); + this.qualityProfileName = qualityProfileDto.getName(); + } + @CheckForNull public String getGroupUuid() { return this.groupUuid; @@ -70,6 +91,8 @@ public class GroupEditorNewValue extends AbstractEditorNewValue { StringBuilder sb = new StringBuilder("{"); addField(sb, "\"qualityGateUuid\": ", this.qualityGateUuid, true); addField(sb, "\"qualityGateName\": ", this.qualityGateName, true); + addField(sb, "\"qualityProfileUuid\": ", this.qualityProfileUuid, true); + addField(sb, "\"qualityProfileName\": ", this.qualityProfileName, true); addField(sb, "\"groupUuid\": ", this.groupUuid, true); addField(sb, "\"groupName\": ", this.groupName, true); endString(sb); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserEditorNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserEditorNewValue.java index 32cca98a725..ed378d0a2cd 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserEditorNewValue.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserEditorNewValue.java @@ -23,6 +23,8 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.db.qualitygate.QualityGateUserPermissionsDto; +import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.db.qualityprofile.QProfileEditUsersDto; import org.sonar.db.user.UserDto; public class UserEditorNewValue extends AbstractEditorNewValue { @@ -38,16 +40,40 @@ public class UserEditorNewValue extends AbstractEditorNewValue { this.userLogin = userLogin; } - public UserEditorNewValue(@Nullable QualityGateDto qualityGateDto, @Nullable UserDto userDto) { - if (qualityGateDto != null) { - this.qualityGateUuid = qualityGateDto.getUuid(); - this.qualityGateName = qualityGateDto.getName(); - } + public UserEditorNewValue(QualityGateDto qualityGateDto, UserDto userDto) { + this.qualityGateUuid = qualityGateDto.getUuid(); + this.qualityGateName = qualityGateDto.getName(); + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } + + public UserEditorNewValue(QualityGateDto qualityGateDto) { + this.qualityGateUuid = qualityGateDto.getUuid(); + this.qualityGateName = qualityGateDto.getName(); + } + + public UserEditorNewValue(UserDto userDto) { + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } + + public UserEditorNewValue(QProfileEditUsersDto qProfileEditUsersDto, String qualityProfileName, String userLogin) { + this.qualityProfileUuid = qProfileEditUsersDto.getQProfileUuid(); + this.qualityProfileName = qualityProfileName; + this.userUuid = qProfileEditUsersDto.getUserUuid(); + this.userLogin = userLogin; + } + + public UserEditorNewValue(QProfileDto qProfileDto, UserDto userDto) { + this.qualityProfileUuid = qProfileDto.getKee(); + this.qualityProfileName = qProfileDto.getName(); + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } - if (userDto != null) { - this.userUuid = userDto.getUuid(); - this.userLogin = userDto.getLogin(); - } + public UserEditorNewValue(QProfileDto qProfileDto) { + this.qualityProfileUuid = qProfileDto.getKee(); + this.qualityProfileName = qProfileDto.getName(); } @CheckForNull @@ -65,6 +91,8 @@ public class UserEditorNewValue extends AbstractEditorNewValue { StringBuilder sb = new StringBuilder("{"); addField(sb, "\"qualityGateUuid\": ", this.qualityGateUuid, true); addField(sb, "\"qualityGateName\": ", this.qualityGateName, true); + addField(sb, "\"qualityProfileUuid\": ", this.qualityProfileUuid, true); + addField(sb, "\"qualityProfileName\": ", this.qualityProfileName, true); addField(sb, "\"userUuid\": ", this.userUuid, true); addField(sb, "\"userLogin\": ", this.userLogin, true); endString(sb); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileDto.java index e6c205c4acb..5fb87f1cbb5 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileDto.java @@ -20,6 +20,7 @@ package org.sonar.db.qualityprofile; import java.util.Date; +import java.util.Objects; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.core.util.UtcDateUtils; @@ -27,7 +28,7 @@ import org.sonar.core.util.UtcDateUtils; /** * Represents the join of "org_qprofiles" and "rules_profiles" */ -public class QProfileDto { +public class QProfileDto implements Comparable { private String kee; private String name; private String language; @@ -139,4 +140,26 @@ public class QProfileDto { .setLastUsed(org.getLastUsed()) .setUserUpdatedAt(org.getUserUpdatedAt()); } + + @Override + public int compareTo(QProfileDto o) { + return kee.compareTo(o.kee); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + QProfileDto that = (QProfileDto) o; + return kee.equals(that.kee); + } + + @Override + public int hashCode() { + return Objects.hash(kee); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java index 484a406dd0d..a86a90537a9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsDao.java @@ -27,6 +27,8 @@ import org.sonar.db.Dao; import org.sonar.db.DatabaseUtils; import org.sonar.db.DbSession; import org.sonar.db.Pagination; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.GroupEditorNewValue; import org.sonar.db.user.GroupDto; import org.sonar.db.user.SearchGroupMembershipDto; @@ -37,9 +39,11 @@ import static org.sonar.db.DatabaseUtils.executeLargeUpdates; public class QProfileEditGroupsDao implements Dao { private final System2 system2; + private final AuditPersister auditPersister; - public QProfileEditGroupsDao(System2 system2) { + public QProfileEditGroupsDao(System2 system2, AuditPersister auditPersister) { this.system2 = system2; + this.auditPersister = auditPersister; } public boolean exists(DbSession dbSession, QProfileDto profile, GroupDto group) { @@ -64,20 +68,40 @@ public class QProfileEditGroupsDao implements Dao { g -> mapper(dbSession).selectQProfileUuidsByGroups(g)); } - public void insert(DbSession dbSession, QProfileEditGroupsDto dto) { + public void insert(DbSession dbSession, QProfileEditGroupsDto dto, String qualityProfileName, String groupName) { mapper(dbSession).insert(dto, system2.now()); + auditPersister.addQualityProfileEditor(dbSession, new GroupEditorNewValue(dto, qualityProfileName, groupName)); } public void deleteByQProfileAndGroup(DbSession dbSession, QProfileDto profile, GroupDto group) { - mapper(dbSession).delete(profile.getKee(), group.getUuid()); + int deletedRows = mapper(dbSession).delete(profile.getKee(), group.getUuid()); + + if (deletedRows > 0) { + auditPersister.deleteQualityProfileEditor(dbSession, new GroupEditorNewValue(profile, group)); + } } public void deleteByQProfiles(DbSession dbSession, List qProfiles) { - executeLargeUpdates(qProfiles.stream().map(QProfileDto::getKee).collect(toList()), p -> mapper(dbSession).deleteByQProfiles(p)); + executeLargeUpdates(qProfiles, + partitionedProfiles -> + { + int deletedRows = mapper(dbSession).deleteByQProfiles(partitionedProfiles + .stream() + .map(QProfileDto::getKee) + .collect(toList())); + + if (deletedRows > 0) { + partitionedProfiles.forEach(p -> auditPersister.deleteQualityProfileEditor(dbSession, new GroupEditorNewValue(p))); + } + }); } public void deleteByGroup(DbSession dbSession, GroupDto group) { - mapper(dbSession).deleteByGroup(group.getUuid()); + int deletedRows = mapper(dbSession).deleteByGroup(group.getUuid()); + + if (deletedRows > 0) { + auditPersister.deleteQualityProfileEditor(dbSession, new GroupEditorNewValue(group)); + } } private static QProfileEditGroupsMapper mapper(DbSession dbSession) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java index e849f4c984b..a20fe731f7d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditGroupsMapper.java @@ -37,10 +37,10 @@ public interface QProfileEditGroupsMapper { void insert(@Param("dto") QProfileEditGroupsDto dto, @Param("now") long now); - void delete(@Param("qProfileUuid") String qProfileUuid, @Param("groupUuid") String groupUuid); + int delete(@Param("qProfileUuid") String qProfileUuid, @Param("groupUuid") String groupUuid); - void deleteByQProfiles(@Param("qProfileUuids") Collection qProfileUuids); + int deleteByQProfiles(@Param("qProfileUuids") Collection qProfileUuids); - void deleteByGroup(@Param("groupUuid") String groupUuid); + int deleteByGroup(@Param("groupUuid") String groupUuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java index 911867520a1..88a189f756e 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java @@ -24,6 +24,8 @@ import org.sonar.api.utils.System2; import org.sonar.db.Dao; import org.sonar.db.DbSession; import org.sonar.db.Pagination; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserEditorNewValue; import org.sonar.db.user.SearchUserMembershipDto; import org.sonar.db.user.UserDto; @@ -33,9 +35,11 @@ import static org.sonar.db.DatabaseUtils.executeLargeUpdates; public class QProfileEditUsersDao implements Dao { private final System2 system2; + private final AuditPersister auditPersister; - public QProfileEditUsersDao(System2 system2) { + public QProfileEditUsersDao(System2 system2, AuditPersister auditPersister) { this.system2 = system2; + this.auditPersister = auditPersister; } public boolean exists(DbSession dbSession, QProfileDto profile, UserDto user) { @@ -54,20 +58,40 @@ public class QProfileEditUsersDao implements Dao { return mapper(dbSession).selectQProfileUuidsByUser(userDto.getUuid()); } - public void insert(DbSession dbSession, QProfileEditUsersDto dto) { + public void insert(DbSession dbSession, QProfileEditUsersDto dto, String qualityProfileName, String userLogin) { mapper(dbSession).insert(dto, system2.now()); + auditPersister.addQualityProfileEditor(dbSession, new UserEditorNewValue(dto, qualityProfileName, userLogin)); } public void deleteByQProfileAndUser(DbSession dbSession, QProfileDto profile, UserDto user) { - mapper(dbSession).delete(profile.getKee(), user.getUuid()); + int deletedRows = mapper(dbSession).delete(profile.getKee(), user.getUuid()); + + if (deletedRows > 0) { + auditPersister.deleteQualityProfileEditor(dbSession, new UserEditorNewValue(profile, user)); + } } public void deleteByQProfiles(DbSession dbSession, List qProfiles) { - executeLargeUpdates(qProfiles.stream().map(QProfileDto::getKee).collect(toList()), p -> mapper(dbSession).deleteByQProfiles(p)); + executeLargeUpdates(qProfiles, + partitionedProfiles -> + { + int deletedRows = mapper(dbSession).deleteByQProfiles(partitionedProfiles + .stream() + .map(QProfileDto::getKee) + .collect(toList())); + + if (deletedRows > 0) { + partitionedProfiles.forEach(p -> auditPersister.deleteQualityProfileEditor(dbSession, new UserEditorNewValue(p))); + } + }); } public void deleteByUser(DbSession dbSession, UserDto user) { - mapper(dbSession).deleteByUser(user.getUuid()); + int deletedRows = mapper(dbSession).deleteByUser(user.getUuid()); + + if (deletedRows > 0) { + auditPersister.deleteQualityProfileEditor(dbSession, new UserEditorNewValue(user)); + } } private static QProfileEditUsersMapper mapper(DbSession dbSession) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java index fdd09a5cc8b..1af27baf9ae 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java @@ -37,9 +37,9 @@ public interface QProfileEditUsersMapper { void insert(@Param("dto") QProfileEditUsersDto dto, @Param("now") long now); - void delete(@Param("qProfileUuid") String qProfileUuid, @Param("userUuid") String userUuid); + int delete(@Param("qProfileUuid") String qProfileUuid, @Param("userUuid") String userUuid); - void deleteByQProfiles(@Param("qProfileUuids") Collection qProfileUuids); + int deleteByQProfiles(@Param("qProfileUuids") Collection qProfileUuids); - void deleteByUser(@Param("userUuid") String userUuid); + int deleteByUser(@Param("userUuid") String userUuid); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java index b90b8e20795..919f596e8fd 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditGroupsDaoTest.java @@ -19,12 +19,16 @@ */ package org.sonar.db.qualityprofile; +import java.util.List; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.Pagination; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.GroupEditorNewValue; import org.sonar.db.user.GroupDto; import org.sonar.db.user.SearchGroupMembershipDto; @@ -34,6 +38,10 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT; @@ -43,10 +51,13 @@ public class QProfileEditGroupsDaoTest { private static final long NOW = 10_000_000_000L; + private final AuditPersister auditPersister = mock(AuditPersister.class); + private final ArgumentCaptor newValueCaptor = ArgumentCaptor.forClass(GroupEditorNewValue.class); + private System2 system2 = new TestSystem2().setNow(NOW); @Rule - public DbTester db = DbTester.create(system2); + public DbTester db = DbTester.create(system2, auditPersister); private QProfileEditGroupsDao underTest = db.getDbClient().qProfileEditGroupsDao(); @@ -201,22 +212,37 @@ public class QProfileEditGroupsDaoTest { .containsExactlyInAnyOrder(profile1.getKee(), profile2.getKee()); assertThat(underTest.selectQProfileUuidsByGroups(db.getSession(), asList(group1, group2, group3))) .containsExactlyInAnyOrder(profile1.getKee(), profile2.getKee()); - assertThat(underTest.selectQProfileUuidsByGroups(db.getSession(),emptyList())).isEmpty(); + assertThat(underTest.selectQProfileUuidsByGroups(db.getSession(), emptyList())).isEmpty(); } @Test public void insert() { + String qualityProfileName = "QPROFILE_NAME"; + String qualityProfileKee = "QPROFILE"; + String groupUuid = "100"; + String groupName = "GROUP_NAME"; underTest.insert(db.getSession(), new QProfileEditGroupsDto() - .setUuid("ABCD") - .setGroupUuid("100") - .setQProfileUuid("QPROFILE") + .setUuid("ABCD") + .setGroupUuid(groupUuid) + .setQProfileUuid(qualityProfileKee), + qualityProfileName, + groupName ); + verify(auditPersister).addQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + GroupEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileUuid, + GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) + .containsExactly(qualityProfileName, qualityProfileKee, groupName, groupUuid); + assertThat(newValue.toString()).contains("\"qualityProfileName\"").contains("\"groupName\""); + assertThat(db.selectFirst(db.getSession(), "select uuid as \"uuid\", group_uuid as \"groupUuid\", qprofile_uuid as \"qProfileUuid\", created_at as \"createdAt\" from qprofile_edit_groups")).contains( entry("uuid", "ABCD"), - entry("groupUuid", "100"), - entry("qProfileUuid", "QPROFILE"), + entry("groupUuid", groupUuid), + entry("qProfileUuid", qualityProfileKee), entry("createdAt", NOW)); } @@ -229,6 +255,15 @@ public class QProfileEditGroupsDaoTest { underTest.deleteByQProfileAndGroup(db.getSession(), profile, group); + verify(auditPersister).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + GroupEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileName, + GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) + .containsExactly(profile.getName(), profile.getKee(), group.getName(), group.getUuid()); + assertThat(newValue.toString()).contains("\"qualityProfileName\"").contains("\"groupName\""); + assertThat(underTest.exists(db.getSession(), profile, group)).isFalse(); } @@ -245,6 +280,20 @@ public class QProfileEditGroupsDaoTest { underTest.deleteByQProfiles(db.getSession(), asList(profile1, profile2)); + verify(auditPersister, times(2)).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + List newValues = newValueCaptor.getAllValues(); + assertThat(newValues.get(0)) + .extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileUuid, + GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) + .containsExactly(profile1.getName(), profile1.getKee(), null, null); + assertThat(newValues.get(0).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); + assertThat(newValues.get(1)) + .extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileUuid, + GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) + .containsExactly(profile2.getName(), profile2.getKee(), null, null); + assertThat(newValues.get(1).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); + assertThat(underTest.exists(db.getSession(), profile1, group1)).isFalse(); assertThat(underTest.exists(db.getSession(), profile2, group2)).isFalse(); assertThat(underTest.exists(db.getSession(), profile3, group1)).isTrue(); @@ -263,6 +312,15 @@ public class QProfileEditGroupsDaoTest { underTest.deleteByGroup(db.getSession(), group1); + verify(auditPersister).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + GroupEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(GroupEditorNewValue::getQualityProfileName, GroupEditorNewValue::getQualityProfileName, + GroupEditorNewValue::getGroupName, GroupEditorNewValue::getGroupUuid) + .containsExactly(null, null, group1.getName(), group1.getUuid()); + assertThat(newValue.toString()).doesNotContain("\"qualityProfileName\"").contains("\"groupName\""); + assertThat(underTest.exists(db.getSession(), profile1, group1)).isFalse(); assertThat(underTest.exists(db.getSession(), profile2, group2)).isTrue(); assertThat(underTest.exists(db.getSession(), profile3, group1)).isFalse(); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java index c68922dd5a7..8989a72e6ac 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java @@ -20,13 +20,17 @@ package org.sonar.db.qualityprofile; import java.sql.SQLException; +import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; import org.sonar.api.impl.utils.TestSystem2; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.Pagination; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserEditorNewValue; import org.sonar.db.user.SearchUserMembershipDto; import org.sonar.db.user.UserDto; @@ -34,6 +38,10 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.ANY; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.IN; import static org.sonar.db.qualityprofile.SearchQualityProfilePermissionQuery.OUT; @@ -45,11 +53,13 @@ public class QProfileEditUsersDaoTest { private static final long NOW = 10_000_000_000L; private final System2 system2 = new TestSystem2().setNow(NOW); + private final AuditPersister auditPersister = mock(AuditPersister.class); + private final ArgumentCaptor newValueCaptor = ArgumentCaptor.forClass(UserEditorNewValue.class); @Rule public ExpectedException expectedException = ExpectedException.none(); @Rule - public DbTester db = DbTester.create(system2); + public DbTester db = DbTester.create(system2, auditPersister); private final QProfileEditUsersDao underTest = db.getDbClient().qProfileEditUsersDao(); @@ -61,6 +71,15 @@ public class QProfileEditUsersDaoTest { UserDto anotherUser = db.users().insertUser(); db.qualityProfiles().addUserPermission(profile, user); + verify(auditPersister).addQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + UserEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, + UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) + .containsExactly(profile.getName(), profile.getKee(), user.getLogin(), user.getUuid()); + assertThat(newValue.toString()).contains("\"qualityProfileName\"").contains("\"userLogin\""); + assertThat(underTest.exists(db.getSession(), profile, user)).isTrue(); assertThat(underTest.exists(db.getSession(), profile, anotherUser)).isFalse(); assertThat(underTest.exists(db.getSession(), anotherProfile, user)).isFalse(); @@ -79,17 +98,17 @@ public class QProfileEditUsersDaoTest { assertThat(underTest.countByQuery(db.getSession(), builder() .setProfile(profile) .setMembership(ANY).build())) - .isEqualTo(3); + .isEqualTo(3); assertThat(underTest.countByQuery(db.getSession(), builder() .setProfile(profile) .setMembership(IN).build())) - .isEqualTo(2); + .isEqualTo(2); assertThat(underTest.countByQuery(db.getSession(), builder() .setProfile(profile) .setMembership(OUT).build())) - .isEqualTo(1); + .isEqualTo(1); } @Test @@ -105,24 +124,24 @@ public class QProfileEditUsersDaoTest { .setProfile(profile) .setMembership(ANY).build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected) - .containsExactlyInAnyOrder( - tuple(user1.getUuid(), true), - tuple(user2.getUuid(), true), - tuple(user3.getUuid(), false)); + .containsExactlyInAnyOrder( + tuple(user1.getUuid(), true), + tuple(user2.getUuid(), true), + tuple(user3.getUuid(), false)); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) .setMembership(IN).build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected) - .containsExactlyInAnyOrder(tuple(user1.getUuid(), true), tuple(user2.getUuid(), true)); + .containsExactlyInAnyOrder(tuple(user1.getUuid(), true), tuple(user2.getUuid(), true)); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) .setMembership(OUT).build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid, SearchUserMembershipDto::isSelected) - .containsExactlyInAnyOrder(tuple(user3.getUuid(), false)); + .containsExactlyInAnyOrder(tuple(user3.getUuid(), false)); } @Test @@ -141,7 +160,7 @@ public class QProfileEditUsersDaoTest { .setQuery("user2").build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactlyInAnyOrder(user2.getUuid()); + .containsExactlyInAnyOrder(user2.getUuid()); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) @@ -149,7 +168,7 @@ public class QProfileEditUsersDaoTest { .setQuery("joh").build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactlyInAnyOrder(user1.getUuid(), user2.getUuid()); + .containsExactlyInAnyOrder(user1.getUuid(), user2.getUuid()); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) @@ -157,7 +176,7 @@ public class QProfileEditUsersDaoTest { .setQuery("Doe").build(), Pagination.all())) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactlyInAnyOrder(user1.getUuid(), user3.getUuid()); + .containsExactlyInAnyOrder(user1.getUuid(), user3.getUuid()); } @Test @@ -175,7 +194,7 @@ public class QProfileEditUsersDaoTest { .build(), Pagination.forPage(1).andSize(1))) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactly(user1.getUuid()); + .containsExactly(user1.getUuid()); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) @@ -183,7 +202,7 @@ public class QProfileEditUsersDaoTest { .build(), Pagination.forPage(3).andSize(1))) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactly(user3.getUuid()); + .containsExactly(user3.getUuid()); assertThat(underTest.selectByQuery(db.getSession(), builder() .setProfile(profile) @@ -191,7 +210,7 @@ public class QProfileEditUsersDaoTest { .build(), Pagination.forPage(1).andSize(10))) .extracting(SearchUserMembershipDto::getUserUuid) - .containsExactly(user1.getUuid(), user2.getUuid(), user3.getUuid()); + .containsExactly(user1.getUuid(), user2.getUuid(), user3.getUuid()); } @Test @@ -210,32 +229,43 @@ public class QProfileEditUsersDaoTest { @Test public void insert() { + String qualityProfileUuid = "QPROFILE"; + String qualityProfileName = "QPROFILE_NAME"; + String userUuid = "100"; + String userLogin = "USER_LOGIN"; underTest.insert(db.getSession(), new QProfileEditUsersDto() - .setUuid("ABCD") - .setUserUuid("100") - .setQProfileUuid("QPROFILE")); + .setUuid("ABCD") + .setUserUuid(userUuid) + .setQProfileUuid(qualityProfileUuid), + qualityProfileName, userLogin); assertThat(db.selectFirst(db.getSession(), "select uuid as \"uuid\", user_uuid as \"userUuid\", qprofile_uuid as \"qProfileUuid\", created_at as \"createdAt\" from qprofile_edit_users")).contains( - entry("uuid", "ABCD"), - entry("userUuid", "100"), - entry("qProfileUuid", "QPROFILE"), - entry("createdAt", NOW)); + entry("uuid", "ABCD"), + entry("userUuid", userUuid), + entry("qProfileUuid", qualityProfileUuid), + entry("createdAt", NOW)); } @Test public void fail_to_insert_same_row_twice() { + String qualityProfileUuid = "QPROFILE"; + String qualityProfileName = "QPROFILE_NAME"; + String userUuid = "100"; + String userLogin = "USER_LOGIN"; underTest.insert(db.getSession(), new QProfileEditUsersDto() - .setUuid("UUID-1") - .setUserUuid("100") - .setQProfileUuid("QPROFILE")); + .setUuid("UUID-1") + .setUserUuid(userUuid) + .setQProfileUuid(qualityProfileUuid), + qualityProfileName, userLogin); expectedException.expectCause(hasType(SQLException.class)); underTest.insert(db.getSession(), new QProfileEditUsersDto() - .setUuid("UUID-2") - .setUserUuid("100") - .setQProfileUuid("QPROFILE")); + .setUuid("UUID-2") + .setUserUuid(userUuid) + .setQProfileUuid(qualityProfileUuid), + qualityProfileName, userLogin); } @Test @@ -247,6 +277,15 @@ public class QProfileEditUsersDaoTest { underTest.deleteByQProfileAndUser(db.getSession(), profile, user); + verify(auditPersister).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + UserEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, + UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) + .containsExactly(profile.getName(), profile.getKee(), user.getLogin(), user.getUuid()); + assertThat(newValue.toString()).contains("\"qualityProfileName\"").contains("\"userLogin\""); + assertThat(underTest.exists(db.getSession(), profile, user)).isFalse(); } @@ -263,6 +302,20 @@ public class QProfileEditUsersDaoTest { underTest.deleteByQProfiles(db.getSession(), asList(profile1, profile2)); + verify(auditPersister, times(2)).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + List newValues = newValueCaptor.getAllValues(); + assertThat(newValues.get(0)) + .extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, + UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) + .containsExactly(profile1.getName(), profile1.getKee(), null, null); + assertThat(newValues.get(0).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); + assertThat(newValues.get(1)) + .extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, + UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) + .containsExactly(profile2.getName(), profile2.getKee(), null, null); + assertThat(newValues.get(1).toString()).contains("\"qualityProfileName\"").doesNotContain("\"groupName\""); + assertThat(underTest.exists(db.getSession(), profile1, user1)).isFalse(); assertThat(underTest.exists(db.getSession(), profile2, user2)).isFalse(); assertThat(underTest.exists(db.getSession(), profile3, user1)).isTrue(); @@ -279,6 +332,15 @@ public class QProfileEditUsersDaoTest { underTest.deleteByUser(db.getSession(), user1); + verify(auditPersister).deleteQualityProfileEditor(eq(db.getSession()), newValueCaptor.capture()); + + UserEditorNewValue newValue = newValueCaptor.getValue(); + assertThat(newValue) + .extracting(UserEditorNewValue::getQualityProfileName, UserEditorNewValue::getQualityProfileUuid, + UserEditorNewValue::getUserLogin, UserEditorNewValue::getUserUuid) + .containsExactly(null, null, user1.getLogin(), user1.getUuid()); + assertThat(newValue.toString()).doesNotContain("\"qualityProfileName\"").contains("\"userLogin\""); + assertThat(underTest.exists(db.getSession(), profile1, user1)).isFalse(); assertThat(underTest.exists(db.getSession(), profile3, user2)).isTrue(); } diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java index 45f859a97c2..43f30fe9a0d 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java @@ -106,29 +106,30 @@ public class QualityProfileDbTester { public QualityProfileDbTester setAsDefault(QProfileDto profile, QProfileDto... others) { dbClient.defaultQProfileDao().insertOrUpdate(dbSession, DefaultQProfileDto.from(profile)); for (QProfileDto other : others) { - dbClient.defaultQProfileDao().insertOrUpdate(dbSession, DefaultQProfileDto.from( other)); + dbClient.defaultQProfileDao().insertOrUpdate(dbSession, DefaultQProfileDto.from(other)); } dbSession.commit(); return this; } - public void addUserPermission(QProfileDto profile, UserDto user){ + public void addUserPermission(QProfileDto profile, UserDto user) { checkArgument(!profile.isBuiltIn(), "Built-In profile cannot be used"); dbClient.qProfileEditUsersDao().insert(dbSession, new QProfileEditUsersDto() - .setUuid(Uuids.createFast()) - .setUserUuid(user.getUuid()) - .setQProfileUuid(profile.getKee()) + .setUuid(Uuids.createFast()) + .setUserUuid(user.getUuid()) + .setQProfileUuid(profile.getKee()), + profile.getName(), user.getLogin() ); dbSession.commit(); } - public void addGroupPermission(QProfileDto profile, GroupDto group){ + public void addGroupPermission(QProfileDto profile, GroupDto group) { checkArgument(!profile.isBuiltIn(), "Built-In profile cannot be used"); dbClient.qProfileEditGroupsDao().insert(dbSession, new QProfileEditGroupsDto() - .setUuid(Uuids.createFast()) - .setGroupUuid(group.getUuid()) - .setQProfileUuid(profile.getKee()) - ); + .setUuid(Uuids.createFast()) + .setGroupUuid(group.getUuid()) + .setQProfileUuid(profile.getKee()), + profile.getName(), group.getName()); dbSession.commit(); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java index a39f48f9394..48d4a198389 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddGroupAction.java @@ -103,7 +103,9 @@ public class AddGroupAction implements QProfileWsAction { new QProfileEditGroupsDto() .setUuid(uuidFactory.create()) .setGroupUuid(group.getUuid()) - .setQProfileUuid(profile.getKee())); + .setQProfileUuid(profile.getKee()), + profile.getName(), + group.getName()); dbSession.commit(); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java index 9472ff01bce..3115a019ab2 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java @@ -104,7 +104,8 @@ public class AddUserAction implements QProfileWsAction { new QProfileEditUsersDto() .setUuid(uuidFactory.create()) .setUserUuid(user.getUuid()) - .setQProfileUuid(profile.getKee())); + .setQProfileUuid(profile.getKee()), + profile.getName(), user.getLogin()); dbSession.commit(); } -- 2.39.5