Skip to content

Commit

Permalink
feature: audit trail on custom list value insertion
Browse files Browse the repository at this point in the history
  • Loading branch information
Pascal-Delange committed Apr 3, 2024
1 parent 59f8f37 commit 48bbc08
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 9 deletions.
8 changes: 6 additions & 2 deletions mocks/custom_list_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ func (cl *CustomListRepository) SoftDeleteCustomList(ctx context.Context, exec r
return args.Error(0)
}

func (cl *CustomListRepository) AddCustomListValue(ctx context.Context, exec repositories.Executor,
addCustomListValue models.AddCustomListValueInput, newCustomListId string,
func (cl *CustomListRepository) AddCustomListValue(
ctx context.Context,
exec repositories.Executor,
addCustomListValue models.AddCustomListValueInput,
newCustomListId string,
userId *models.UserId,
) error {
args := cl.Called(exec, addCustomListValue)
return args.Error(0)
Expand Down
19 changes: 16 additions & 3 deletions repositories/custom_list_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ type CustomListRepository interface {
organizationId string, newCustomListId string) error
UpdateCustomList(ctx context.Context, exec Executor, updateCustomList models.UpdateCustomListInput) error
SoftDeleteCustomList(ctx context.Context, exec Executor, listId string) error
AddCustomListValue(ctx context.Context, exec Executor, addCustomListValue models.AddCustomListValueInput, newCustomListId string) error
AddCustomListValue(
ctx context.Context,
exec Executor,
addCustomListValue models.AddCustomListValueInput,
newCustomListId string,
userId *models.UserId,
) error
DeleteCustomListValue(ctx context.Context, exec Executor, deleteCustomListValue models.DeleteCustomListValueInput) error
}

Expand Down Expand Up @@ -160,12 +166,19 @@ func (repo *CustomListRepositoryPostgresql) AddCustomListValue(
ctx context.Context,
exec Executor,
addCustomListValue models.AddCustomListValueInput,
newCustomListId string,
newId string,
userId *models.UserId,
) error {
if err := validateMarbleDbExecutor(exec); err != nil {
return err
}

if userId != nil {
if err := setCurrentUserIdContext(ctx, exec, userId); err != nil {
return err
}
}

err := ExecBuilder(
ctx,
exec,
Expand All @@ -176,7 +189,7 @@ func (repo *CustomListRepositoryPostgresql) AddCustomListValue(
"value",
).
Values(
newCustomListId,
newId,
addCustomListValue.CustomListId,
addCustomListValue.Value,
),
Expand Down
55 changes: 55 additions & 0 deletions repositories/migrations/20240402153800_audit_trail_trigger.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
-- +goose Up
-- +goose StatementBegin
CREATE TYPE audit_operation AS ENUM('INSERT', 'UPDATE', 'DELETE');

-- CreateTable
CREATE TABLE
audit (
"id" UUID NOT NULL DEFAULT gen_random_uuid () PRIMARY KEY,
"operation" audit_operation NOT NULL,
"user_id" TEXT,
"table" VARCHAR NOT NULL,
"entity_id" UUID NOT NULL,
"data" JSONB NOT NULL DEFAULT '{}',
"created_at" TIMESTAMPTZ (6) NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- Order audit trigger function
CREATE
OR REPLACE FUNCTION global_audit () RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO audit ("operation", "user_id", "table", "entity_id", "data", "created_at")
VALUES ('DELETE', current_setting('custom.current_user_id', TRUE), TG_TABLE_NAME, OLD.id, to_jsonb(OLD), now());

ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO audit ("operation", "user_id", "table", "entity_id", "data", "created_at")
VALUES ('UPDATE', current_setting('custom.current_user_id', TRUE), TG_TABLE_NAME, NEW.id, to_jsonb(NEW), now());

ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO audit ("operation", "user_id", "table", "entity_id", "data", "created_at")
VALUES ('INSERT', current_setting('custom.current_user_id', TRUE), TG_TABLE_NAME, NEW.id, to_jsonb(NEW), now());
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER audit
AFTER INSERT
OR
UPDATE
OR DELETE ON custom_list_values FOR EACH ROW
EXECUTE FUNCTION global_audit ();

-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TRIGGER audit ON custom_list_values;

DROP FUNCTION global_audit;

DROP TABLE audit;

DROP TYPE audit_operation;

-- +goose StatementEnd
16 changes: 16 additions & 0 deletions repositories/utils.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
package repositories

import (
"context"
"fmt"

"github.com/checkmarble/marble-backend/models"
"github.com/checkmarble/marble-backend/pure_utils"
)

const postgres_audit_user_id_parameter = "custom.current_user_id"

func setCurrentUserIdContext(ctx context.Context, exec Executor, userId *models.UserId) error {
if userId != nil {
_, err := exec.Exec(
ctx,
fmt.Sprintf("SELECT SET_CONFIG('%s', $1, false)", postgres_audit_user_id_parameter),
*userId,
)
return err
}
return nil
}

func columnsNames(tablename string, fields []string) []string {
return pure_utils.Map(fields, func(f string) string {
return fmt.Sprintf("%s.%s", tablename, f)
Expand Down
9 changes: 8 additions & 1 deletion usecases/custom_list_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/checkmarble/marble-backend/usecases/executor_factory"
"github.com/checkmarble/marble-backend/usecases/security"
"github.com/checkmarble/marble-backend/usecases/tracking"
"github.com/checkmarble/marble-backend/utils"
)

type CustomListUseCase struct {
Expand Down Expand Up @@ -154,6 +155,12 @@ func (usecase *CustomListUseCase) GetCustomListValues(ctx context.Context,
func (usecase *CustomListUseCase) AddCustomListValue(ctx context.Context,
addCustomListValue models.AddCustomListValueInput,
) (models.CustomListValue, error) {
var userId *models.UserId
creds, found := utils.CredentialsFromCtx(ctx)
if found {
userId = &creds.ActorIdentity.UserId
}

value, err := executor_factory.TransactionReturnValue(ctx, usecase.transactionFactory, func(
tx repositories.Executor,
) (models.CustomListValue, error) {
Expand All @@ -166,7 +173,7 @@ func (usecase *CustomListUseCase) AddCustomListValue(ctx context.Context,
}
newCustomListValueId := uuid.NewString()

err = usecase.CustomListRepository.AddCustomListValue(ctx, tx, addCustomListValue, newCustomListValueId)
err = usecase.CustomListRepository.AddCustomListValue(ctx, tx, addCustomListValue, newCustomListValueId, userId)
if err != nil {
return models.CustomListValue{}, err
}
Expand Down
6 changes: 3 additions & 3 deletions usecases/organization/organization_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ func (creator *OrganizationCreator) seedDefaultList(ctx context.Context, organiz
CustomListId: newCustomListId,
Value: "Welcome",
}
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString())
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString(), nil)
addCustomListValueInput.Value = "to"
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString())
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString(), nil)
addCustomListValueInput.Value = "marble"
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString())
creator.CustomListRepository.AddCustomListValue(ctx, exec, addCustomListValueInput, uuid.NewString(), nil)

logger.InfoContext(ctx, "Finish to create the default custom list for the organization")
return nil
Expand Down

0 comments on commit 48bbc08

Please sign in to comment.