initial audit-log implementation
This commit is contained in:
parent
cb641eb8c6
commit
e880a1c2c8
@ -35,6 +35,7 @@ public class RbacUserController implements RbacusersApi {
|
|||||||
@RequestBody final RbacUserResource body
|
@RequestBody final RbacUserResource body
|
||||||
) {
|
) {
|
||||||
context.setCurrentTask("creating new user: " + body.getName());
|
context.setCurrentTask("creating new user: " + body.getName());
|
||||||
|
context.setCurrentUser(body.getName());
|
||||||
|
|
||||||
if (body.getUuid() == null) {
|
if (body.getUuid() == null) {
|
||||||
body.setUuid(UUID.randomUUID());
|
body.setUuid(UUID.randomUUID());
|
||||||
|
98
src/main/resources/db/changelog/020-audit-log.sql
Normal file
98
src/main/resources/db/changelog/020-audit-log.sql
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset audit-OPERATION-TYPE:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
A type representing a DML operation.
|
||||||
|
*/
|
||||||
|
do $$
|
||||||
|
begin
|
||||||
|
if not exists(select 1 from pg_type where typname = 'operation') then
|
||||||
|
create type "operation" as enum ('INSERT', 'UPDATE', 'DELETE', 'TRUNCATE');
|
||||||
|
end if;
|
||||||
|
--more types here...
|
||||||
|
end $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset audit-TABLE-TX-AUDIT-LOG:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
A table storing the transaction audit log for all target tables.
|
||||||
|
*/
|
||||||
|
create table "tx_audit_log"
|
||||||
|
(
|
||||||
|
txId bigint not null,
|
||||||
|
txTimestamp timestamp not null,
|
||||||
|
currentUser varchar(63) not null, -- TODO.SPEC: Keep user name or uuid in audit-log?
|
||||||
|
assumedRoles varchar not null, -- TODO.SPEC: Store role names or uuids in audit-log?
|
||||||
|
currentTask varchar not null,
|
||||||
|
targetTable text not null,
|
||||||
|
targetUuid uuid not null, -- TODO.SPEC: All audited tables have a uuid column.
|
||||||
|
targetOp operation not null,
|
||||||
|
targetDelta jsonb
|
||||||
|
);
|
||||||
|
|
||||||
|
create index on tx_audit_log using brin (txTimestamp);
|
||||||
|
create index on tx_audit_log (targetTable, targetUuid);
|
||||||
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset audit-TX-AUDIT-TRIGGER:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Trigger function for transaction audit log.
|
||||||
|
*/
|
||||||
|
create or replace function tx_audit_log_trigger()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
case tg_op
|
||||||
|
when 'INSERT' then
|
||||||
|
insert
|
||||||
|
into tx_audit_log
|
||||||
|
values (txid_current(), now(),
|
||||||
|
currentUser(), assumedRoles(), currentTask(),
|
||||||
|
tg_table_name, new.uuid, tg_op::operation,
|
||||||
|
to_jsonb(new));
|
||||||
|
when 'UPDATE' then
|
||||||
|
insert
|
||||||
|
into tx_audit_log
|
||||||
|
values (txid_current(), now(),
|
||||||
|
currentUser(), assumedRoles(), currentTask(),
|
||||||
|
tg_table_name, old.uuid, tg_op::operation,
|
||||||
|
jsonb_changes_delta(to_jsonb(old), to_jsonb(new)));
|
||||||
|
when 'DELETE' then
|
||||||
|
insert
|
||||||
|
into tx_audit_log
|
||||||
|
values (txid_current(), now(),
|
||||||
|
currentUser(), assumedRoles(), currentTask(),
|
||||||
|
tg_table_name, old.uuid, 'DELETE'::operation,
|
||||||
|
null::jsonb);
|
||||||
|
else
|
||||||
|
raise exception 'Trigger op % not supported for %.', tg_op, tg_table_name;
|
||||||
|
end case;
|
||||||
|
return null;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset audit-CREATE-AUDIT-LOG:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
Trigger function for transaction audit log.
|
||||||
|
*/
|
||||||
|
|
||||||
|
create or replace procedure create_audit_log(targetTable varchar)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
createTriggerSQL varchar;
|
||||||
|
begin
|
||||||
|
createTriggerSQL = 'CREATE TRIGGER ' || targetTable || '_audit_log' ||
|
||||||
|
' AFTER INSERT OR UPDATE OR DELETE ON ' || targetTable ||
|
||||||
|
' FOR EACH ROW EXECUTE PROCEDURE tx_audit_log_trigger()';
|
||||||
|
raise notice 'sql: %', createTriggerSQL;
|
||||||
|
execute createTriggerSQL;
|
||||||
|
end; $$;
|
||||||
|
--//
|
@ -26,7 +26,6 @@ begin
|
|||||||
end if;
|
end if;
|
||||||
return expectedType;
|
return expectedType;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
@ -41,6 +40,8 @@ create table RbacUser
|
|||||||
name varchar(63) not null unique
|
name varchar(63) not null unique
|
||||||
);
|
);
|
||||||
|
|
||||||
|
call create_audit_log('RbacUser');
|
||||||
|
|
||||||
create or replace function createRbacUser(userName varchar)
|
create or replace function createRbacUser(userName varchar)
|
||||||
returns uuid
|
returns uuid
|
||||||
returns null on null input
|
returns null on null input
|
||||||
@ -119,6 +120,8 @@ create table RbacObject
|
|||||||
unique (objectTable, uuid)
|
unique (objectTable, uuid)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
call create_audit_log('RbacObject');
|
||||||
|
|
||||||
create or replace function createRbacObject()
|
create or replace function createRbacObject()
|
||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
@ -144,8 +147,6 @@ begin
|
|||||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
end if;
|
end if;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
@ -165,6 +166,8 @@ create table RbacRole
|
|||||||
unique (objectUuid, roleType)
|
unique (objectUuid, roleType)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
call create_audit_log('RbacRole');
|
||||||
|
|
||||||
create type RbacRoleDescriptor as
|
create type RbacRoleDescriptor as
|
||||||
(
|
(
|
||||||
objectTable varchar(63), -- TODO: needed? remove?
|
objectTable varchar(63), -- TODO: needed? remove?
|
||||||
@ -284,6 +287,8 @@ create table RbacPermission
|
|||||||
unique (objectUuid, op)
|
unique (objectUuid, op)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
call create_audit_log('RbacPermission');
|
||||||
|
|
||||||
create or replace function permissionExists(forObjectUuid uuid, forOp RbacOp)
|
create or replace function permissionExists(forObjectUuid uuid, forOp RbacOp)
|
||||||
returns bool
|
returns bool
|
||||||
language sql as $$
|
language sql as $$
|
||||||
@ -353,15 +358,17 @@ $$;
|
|||||||
*/
|
*/
|
||||||
create table RbacGrants
|
create table RbacGrants
|
||||||
(
|
(
|
||||||
|
uuid uuid primary key default uuid_generate_v4(),
|
||||||
grantedByRoleUuid uuid references RbacRole (uuid) on delete cascade,
|
grantedByRoleUuid uuid references RbacRole (uuid) on delete cascade,
|
||||||
ascendantUuid uuid references RbacReference (uuid) on delete cascade,
|
ascendantUuid uuid references RbacReference (uuid) on delete cascade,
|
||||||
descendantUuid uuid references RbacReference (uuid) on delete cascade,
|
descendantUuid uuid references RbacReference (uuid) on delete cascade,
|
||||||
assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false)
|
assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false)
|
||||||
primary key (ascendantUuid, descendantUuid)
|
unique (ascendantUuid, descendantUuid)
|
||||||
);
|
);
|
||||||
create index on RbacGrants (ascendantUuid);
|
create index on RbacGrants (ascendantUuid);
|
||||||
create index on RbacGrants (descendantUuid);
|
create index on RbacGrants (descendantUuid);
|
||||||
|
|
||||||
|
call create_audit_log('RbacGrants');
|
||||||
|
|
||||||
create or replace function findGrantees(grantedId uuid)
|
create or replace function findGrantees(grantedId uuid)
|
||||||
returns setof RbacReference
|
returns setof RbacReference
|
||||||
|
@ -11,6 +11,8 @@ databaseChangeLog:
|
|||||||
file: db/changelog/005-uuid-ossp-extension.sql
|
file: db/changelog/005-uuid-ossp-extension.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/010-context.sql
|
file: db/changelog/010-context.sql
|
||||||
|
- include:
|
||||||
|
file: db/changelog/020-audit-log.sql
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/050-rbac-base.sql
|
file: db/changelog/050-rbac-base.sql
|
||||||
- include:
|
- include:
|
||||||
|
@ -195,9 +195,10 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
@Test
|
@Test
|
||||||
public void packageAdmin_canRevokeOwnPackageAdminRoleGrantedByAnotherAdminOfThatPackage() {
|
public void packageAdmin_canRevokeOwnPackageAdminRoleGrantedByAnotherAdminOfThatPackage() {
|
||||||
// given
|
// given
|
||||||
|
final var newUser = createNewUserTransacted();
|
||||||
final var grant = create(grant()
|
final var grant = create(grant()
|
||||||
.byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.admin")
|
.byUser("admin@aaa.example.com").withAssumedRole("package#aaa00.admin")
|
||||||
.grantingRole("package#aaa00.admin").toUser(createNewUser().getName()));
|
.grantingRole("package#aaa00.admin").toUser(newUser.getName()));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
context("aaa00@aaa.example.com", "package#aaa00.admin");
|
context("aaa00@aaa.example.com", "package#aaa00.admin");
|
||||||
@ -291,6 +292,14 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RbacUserEntity createNewUserTransacted() {
|
||||||
|
return jpaAttempt.transacted(() -> {
|
||||||
|
final var newUserName = "test-user-" + System.currentTimeMillis() + "@example.com";
|
||||||
|
context(newUserName);
|
||||||
|
return rbacUserRepository.create(new RbacUserEntity(null, newUserName));
|
||||||
|
}).assumeSuccessful().returnedValue();
|
||||||
|
}
|
||||||
|
|
||||||
private RbacUserEntity createNewUser() {
|
private RbacUserEntity createNewUser() {
|
||||||
return rbacUserRepository.create(
|
return rbacUserRepository.create(
|
||||||
new RbacUserEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com"));
|
new RbacUserEntity(null, "test-user-" + System.currentTimeMillis() + "@example.com"));
|
||||||
|
Loading…
Reference in New Issue
Block a user