diff options
author | Matteo Mara <matteo.mara@sonarsource.com> | 2022-06-08 21:36:50 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-06-09 20:03:14 +0000 |
commit | 9a650e31353a9f53b8a9ce32f7d3dd54f5297d0c (patch) | |
tree | 86c7e323b166298b50c474707ee4d400b7975c72 | |
parent | d4ad45293740252e9e0ec00aee6ddad4d9fa2fde (diff) | |
download | sonarqube-9a650e31353a9f53b8a9ce32f7d3dd54f5297d0c.tar.gz sonarqube-9a650e31353a9f53b8a9ce32f7d3dd54f5297d0c.zip |
SONAR-15359 Better differentiate user from system operations in audit logs
10 files changed, 296 insertions, 1 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java index 6285e678592..d3bdc7d1906 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java @@ -26,6 +26,7 @@ public class AuditDto { private String uuid; private String userUuid; private String userLogin; + private boolean userTriggered; private String category; private String operation; private String newValue; @@ -55,6 +56,14 @@ public class AuditDto { this.userLogin = userLogin; } + public boolean isUserTriggered() { + return userTriggered; + } + + public void setUserTriggered(boolean userTriggered) { + this.userTriggered = userTriggered; + } + public String getCategory() { return category; } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml index 68275f1316f..f0d9c9249b4 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml @@ -7,6 +7,7 @@ a.uuid as "uuid", a.user_uuid as "userUuid", a.user_login as "userLogin", + a.user_triggered as "userTriggered", a.category as "category", a.operation as "operation", a.new_value as "newValue", @@ -17,6 +18,7 @@ uuid, user_uuid, user_login, + user_triggered, category, operation, new_value, @@ -65,6 +67,7 @@ uuid, user_uuid, user_login, + user_triggered, category, operation, new_value, @@ -74,6 +77,7 @@ #{dto.uuid, jdbcType=VARCHAR}, #{dto.userUuid, jdbcType=VARCHAR}, #{dto.userLogin, jdbcType=VARCHAR}, + #{dto.userTriggered, jdbcType=BOOLEAN}, #{dto.category, jdbcType=VARCHAR}, #{dto.operation, jdbcType=VARCHAR}, #{dto.newValue, jdbcType=VARCHAR}, diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 4aa81a9d442..2effb531681 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -107,7 +107,8 @@ CREATE TABLE "AUDITS"( "CATEGORY" CHARACTER VARYING(25) NOT NULL, "OPERATION" CHARACTER VARYING(50) NOT NULL, "NEW_VALUE" CHARACTER VARYING(4000), - "CREATED_AT" BIGINT NOT NULL + "CREATED_AT" BIGINT NOT NULL, + "USER_TRIGGERED" BOOLEAN DEFAULT TRUE NOT NULL ); ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAudits.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAudits.java new file mode 100644 index 00000000000..ac6f469a09f --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAudits.java @@ -0,0 +1,48 @@ +/* + * 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.v95; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.def.BooleanColumnDef; +import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class AddUserTriggeredColumnToAudits extends DdlChange { + private static final String TABLE_NAME = "audits"; + private static final String COLUMN_NAME = "user_triggered"; + + public AddUserTriggeredColumnToAudits(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection c = getDatabase().getDataSource().getConnection()) { + if (!DatabaseUtils.tableColumnExists(c, TABLE_NAME, COLUMN_NAME)) { + context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) + .addColumn(BooleanColumnDef.newBooleanColumnDefBuilder().setColumnName(COLUMN_NAME).setIsNullable(false).setDefaultValue(true).build()) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java index 9a92d949627..ff1ef92b923 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java @@ -47,6 +47,9 @@ public class DbVersion95 implements DbVersion { .add(6415, "Migrate hotspot rule descriptions", MigrateHotspotRuleDescriptions.class) .add(6416, "Remove onboarded column from User table", DropOnboardedColumnFromUserTable.class) + + .add(6417, "Add column 'user_triggered' to 'audits'", AddUserTriggeredColumnToAudits.class) + .add(6418, "Upsert value of 'user_triggered' in 'audits'", UpsertAuditsUserTriggeredValue.class) ; } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValue.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValue.java new file mode 100644 index 00000000000..341681fef66 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValue.java @@ -0,0 +1,41 @@ +/* + * 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.v95; + +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 UpsertAuditsUserTriggeredValue extends DataChange { + + public UpsertAuditsUserTriggeredValue(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + Upsert upsert = context.prepareUpsert("update audits set user_triggered = ? where user_uuid = '-'"); + upsert + .setBoolean(1, false); + upsert.execute(); + upsert.commit(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest.java new file mode 100644 index 00000000000..b4a20d7359d --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest.java @@ -0,0 +1,52 @@ +/* + * 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.v95; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +public class AddUserTriggeredColumnToAuditsTest { + private static final String TABLE_NAME = "audits"; + private static final String COLUMN_NAME = "user_triggered"; + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(AddUserTriggeredColumnToAuditsTest.class, "schema.sql"); + + private final AddUserTriggeredColumnToAudits underTest = new AddUserTriggeredColumnToAudits(db.database()); + + @Test + public void migration_should_add_column() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, false); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, false); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest.java new file mode 100644 index 00000000000..86ef1f7062f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest.java @@ -0,0 +1,114 @@ +/* + * 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.v95; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; +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.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.assertj.core.api.Assertions.assertThat; + +public class UpsertAuditsUserTriggeredValueTest { + + private final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(UpsertAuditsUserTriggeredValueTest.class, "schema.sql"); + + private final DataChange underTest = new UpsertAuditsUserTriggeredValue(db.database()); + + @Test + public void migration_populates_audits_user_triggered() throws SQLException { + String auditUuid1 = insertUserTriggeredLog(); + String auditUuid2 = insertSystemTriggeredLog(); + + underTest.execute(); + + assertUserTriggeredIsTrue(auditUuid1); + assertUserTriggeredIsFalse(auditUuid2); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + String auditUuid1 = insertUserTriggeredLog(); + String auditUuid2 = insertSystemTriggeredLog(); + + underTest.execute(); + // re-entrant + underTest.execute(); + + assertUserTriggeredIsTrue(auditUuid1); + assertUserTriggeredIsFalse(auditUuid2); + } + + private void assertUserTriggeredIsTrue(String userUuid) { + assertUserTriggered(userUuid,true); + } + + private void assertUserTriggeredIsFalse(String userUuid) { + assertUserTriggered(userUuid,false); + } + + private void assertUserTriggered(String userUuid, boolean userTriggered) { + String selectSql = String.format("select USER_TRIGGERED from audits where uuid='%s'", userUuid); + assertThat(db.select(selectSql).stream().map(row -> row.get("USER_TRIGGERED")).collect(Collectors.toList())) + .containsExactlyInAnyOrder(userTriggered); + } + + private String insertUserTriggeredLog() { + Map<String, Object> map = getAuditsValueMap(uuidFactory.create(), randomAlphabetic(20)); + return insertAuditsValueMap(map); + } + + private String insertSystemTriggeredLog() { + Map<String, Object> map = getAuditsValueMap("-", "System"); + return insertAuditsValueMap(map); + } + + @NotNull + private Map<String, Object> getAuditsValueMap(String userUuid, String userLogin) { + Map<String, Object> map = new HashMap<>(); + String uuid = uuidFactory.create(); + map.put("UUID", uuid); + map.put("USER_UUID", userUuid); + map.put("USER_LOGIN", userLogin); + map.put("CATEGORY", "USER"); + map.put("OPERATION", "UPDATE"); + map.put("CREATED_AT", System.currentTimeMillis()); + map.put("NEW_VALUE", "Some Value"); + map.put("USER_TRIGGERED", true); + return map; + } + + private String insertAuditsValueMap(Map<String, Object> map) { + db.executeInsert("audits", map); + return (String) map.get("UUID"); + } + +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest/schema.sql new file mode 100644 index 00000000000..f8f1e60c211 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest/schema.sql @@ -0,0 +1,11 @@ +CREATE TABLE "AUDITS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "USER_UUID" CHARACTER VARYING(40) NOT NULL, + "USER_LOGIN" CHARACTER VARYING(255) NOT NULL, + "CATEGORY" CHARACTER VARYING(25) NOT NULL, + "OPERATION" CHARACTER VARYING(50) NOT NULL, + "NEW_VALUE" CHARACTER VARYING(4000), + "CREATED_AT" BIGINT NOT NULL +); +ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); +CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest/schema.sql new file mode 100644 index 00000000000..19e834ac833 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest/schema.sql @@ -0,0 +1,12 @@ +CREATE TABLE "AUDITS"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "USER_UUID" CHARACTER VARYING(40) NOT NULL, + "USER_LOGIN" CHARACTER VARYING(255) NOT NULL, + "CATEGORY" CHARACTER VARYING(25) NOT NULL, + "OPERATION" CHARACTER VARYING(50) NOT NULL, + "NEW_VALUE" CHARACTER VARYING(4000), + "CREATED_AT" BIGINT NOT NULL, + "USER_TRIGGERED" BOOLEAN DEFAULT TRUE NOT NULL +); +ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); +CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); |