Browse Source

SONAR-15359 Better differentiate user from system operations in audit logs

tags/9.5.0.56709
Matteo Mara 2 years ago
parent
commit
9a650e3135

+ 9
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java View 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;
}

+ 4
- 0
server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml View 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},

+ 2
- 1
server/sonar-db-dao/src/schema/schema-sq.ddl View 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);

+ 48
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAudits.java View File

@@ -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());
}
}
}
}

+ 3
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/DbVersion95.java View 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)
;
}
}

+ 41
- 0
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValue.java View File

@@ -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();
}
}

+ 52
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest.java View File

@@ -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);
}

}

+ 114
- 0
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest.java View File

@@ -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");
}

}

+ 11
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/AddUserTriggeredColumnToAuditsTest/schema.sql View File

@@ -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);

+ 12
- 0
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v95/UpsertAuditsUserTriggeredValueTest/schema.sql View File

@@ -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);

Loading…
Cancel
Save