diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java index adcb1c36..d5266ae9 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.rbac.rbacdef; import java.util.Optional; +import java.util.stream.Stream; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE; @@ -95,11 +96,7 @@ public class InsertTriggerGenerator { } private void generateInsertCheckTrigger(final StringWriter plPgSql) { - rbacDef.getGrantDefs().stream() - .filter(g -> g.isToCreate() && g.grantType() == PERM_TO_ROLE && - g.getPermDef().getPermission() == INSERT ) - .forEach(g -> { - plPgSql.writeLn(""" + plPgSql.writeLn(""" /** Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}. */ @@ -107,24 +104,51 @@ public class InsertTriggerGenerator { returns trigger language plpgsql as $$ begin - raise exception 'insert into ${rawSubTable} not allowed for current subjects %', currentSubjectsUuids(); + raise exception 'insert into ${rawSubTable} not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); end; $$; - - create trigger ${rawSubTable}_insert_permission_check_tg - before insert on ${rawSubTable} - for each row - when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') ) - execute procedure ${rawSubTable}_insert_permission_missing_tf(); """, - with("rawSubTable", g.getPermDef().entityAlias.getRawTableName()), - with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() )); - }); + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); + getOptionalInsertGrant().ifPresentOrElse(g -> { + plPgSql.writeLn(""" + create trigger ${rawSubTable}_insert_permission_check_tg + before insert on ${rawSubTable} + for each row + when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') ) + execute procedure ${rawSubTable}_insert_permission_missing_tf(); + """, + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()), + with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() )); + }, + () -> { + plPgSql.writeLn(""" + create trigger ${rawSubTable}_insert_permission_check_tg + before insert on ${rawSubTable} + for each row + -- As there is no explicit INSERT grant specified for this table, + -- only global admins are allowed to insert any rows. + when ( not isGlobalAdmin() ) + execute procedure ${rawSubTable}_insert_permission_missing_tf(); + """, + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); + }); + } + + private Stream getInsertGrants() { + return rbacDef.getGrantDefs().stream() + .filter(g -> g.grantType() == PERM_TO_ROLE) + .filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT); + } + + private Optional getOptionalInsertGrant() { + return getInsertGrants() + .reduce((x, y) -> { + throw new IllegalStateException("only a single INSERT permission grant allowed"); + }); } private Optional getOptionalInsertSuperRole() { - return rbacDef.getGrantDefs().stream() - .filter(g -> g.grantType() == PERM_TO_ROLE) - .filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT) + return getInsertGrants() .map(RbacView.RbacGrantDefinition::getSuperRoleDef) .reduce((x, y) -> { throw new IllegalStateException("only a single INSERT permission grant allowed"); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java index 624ee471..4f1bffe3 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -334,6 +334,7 @@ class RolesGrantsAndPermissionsGenerator { .map(RbacPermissionDefinition::getPermission) .map(RbacView.Permission::permission) .map(p -> "'" + p + "'") + .sorted() .toList(); plPgSql.indented(() -> plPgSql.writeLn("permissions => array[" + joinArrayElements(arrayElements, 3) + "],\n")); diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql index 034400fa..a262791c 100644 --- a/src/main/resources/db/changelog/080-rbac-global.sql +++ b/src/main/resources/db/changelog/080-rbac-global.sql @@ -22,6 +22,19 @@ grant select on global to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// +-- ============================================================================ +--changeset rbac-global-IS-GLOBAL-ADMIN:1 endDelimiter:--// +-- ------------------------------------------------------------------ + +create or replace function isGlobalAdmin() + returns boolean + language plpgsql as $$ +begin + return isGranted(currentSubjectsUuids(), findRoleId(globalAdmin())); +end; $$; +--// + + -- ============================================================================ --changeset rbac-global-HAS-GLOBAL-PERMISSION:1 endDelimiter:--// -- ------------------------------------------------------------------ diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/113-test-customer-rbac.sql index 1e5573cc..3149ccc9 100644 --- a/src/main/resources/db/changelog/113-test-customer-rbac.sql +++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-06T15:40:13.239729250. +-- This code generated was by RbacViewPostgresGenerator at 2024-03-07T12:25:36.376742633. -- ============================================================================ @@ -80,6 +80,25 @@ execute procedure insertTriggerForTestCustomer_tf(); --changeset test-customer-rbac-INSERT:1 endDelimiter:--// -- ---------------------------------------------------------------------------- +/** + Checks if the user or assumed roles are allowed to insert a row to test_customer. +*/ +create or replace function test_customer_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception 'insert into test_customer not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger test_customer_insert_permission_check_tg + before insert on test_customer + for each row + -- As there is no explicit INSERT grant specified for this table, + -- only global admins are allowed to insert any rows. + when ( not isGlobalAdmin() ) + execute procedure test_customer_insert_permission_missing_tf(); + --// -- ============================================================================ diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql index fadb2562..b32a98dc 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-06T15:40:13.277446553. +-- This code generated was by RbacViewPostgresGenerator at 2024-03-07T12:25:36.422351715. -- ============================================================================ @@ -194,7 +194,8 @@ create or replace function test_package_insert_permission_missing_tf() returns trigger language plpgsql as $$ begin - raise exception 'insert into test_package not allowed for current subjects %', currentSubjectsUuids(); + raise exception 'insert into test_package not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); end; $$; create trigger test_package_insert_permission_check_tg diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java index b21f2d5d..01f09d26 100644 --- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java @@ -74,7 +74,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest { // then result.assertExceptionWithRootCauseMessage( PersistenceException.class, - "add-customer not permitted for test_customer#xxx.admin"); + "ERROR: insert into test_customer not allowed for current subjects {test_customer#xxx.admin}"); } @Test @@ -92,7 +92,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest { // then result.assertExceptionWithRootCauseMessage( PersistenceException.class, - "add-customer not permitted for customer-admin@xxx.example.com"); + "ERROR: insert into test_customer not allowed for current subjects {customer-admin@xxx.example.com}"); }