]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15359 Better differentiate user from system operations in audit logs
authorMatteo Mara <matteo.mara@sonarsource.com>
Wed, 8 Jun 2022 19:36:50 +0000 (21:36 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 9 Jun 2022 20:03:14 +0000 (20:03 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAudits.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValue.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest/schema.sql [new file with mode: 0644]

index 6285e678592035ccc4dbf8e190a4141e6ff99a10..d3bdc7d1906d95f46fb1bb94ac4fa81268ab883e 100644 (file)
@@ -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;
   }
index 68275f1316fcb5ed5770f261dc4d01656cc10486..f0d9c9249b4890b43f99ec3a20a888b7784578aa 100644 (file)
@@ -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},
index 4aa81a9d442474992f2dc945900b7663a4f9b8e3..2effb531681ff31e445bd2fb810cc489672629aa 100644 (file)
@@ -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 (file)
index 0000000..ac6f469
--- /dev/null
@@ -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());
+      }
+    }
+  }
+}
index 9a92d94962783407b8f7b17a962137b80098449d..ff1ef92b923fdc49cf45d9b1003cbe8ef4c9b9d0 100644 (file)
@@ -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 (file)
index 0000000..341681f
--- /dev/null
@@ -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 (file)
index 0000000..b4a20d7
--- /dev/null
@@ -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 (file)
index 0000000..86ef1f7
--- /dev/null
@@ -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 (file)
index 0000000..f8f1e60
--- /dev/null
@@ -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 (file)
index 0000000..19e834a
--- /dev/null
@@ -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);