From 76b98eab2eefec5173b7d26e1713dc755b319079 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 12 Mar 2024 16:34:16 +0100 Subject: [PATCH] test-data-generation working up to debitor, fails in membership --- .../office/debitor/HsOfficeDebitorEntity.java | 35 +- .../partner/HsOfficePartnerDetailsEntity.java | 2 +- .../office/partner/HsOfficePartnerEntity.java | 3 +- .../rbac/rbacdef/InsertTriggerGenerator.java | 2 +- .../hsadminng/rbac/rbacdef/RbacView.java | 49 ++- .../rbacdef/RbacViewPostgresGenerator.java | 7 +- .../RolesGrantsAndPermissionsGenerator.java | 43 +- .../hsadminng/rbac/rbacdef/StringWriter.java | 4 +- .../resources/db/changelog/050-rbac-base.sql | 11 +- .../db/changelog/123-test-package-rbac.sql | 6 +- .../db/changelog/133-test-domain-rbac.sql | 6 +- .../changelog/213-hs-office-person-rbac.sql | 6 +- .../228-hs-office-relationship-test-data.sql | 2 +- .../243-hs-office-bankaccount-rbac.sql | 6 +- .../changelog/273-hs-office-debitor-rbac.md | 323 ++++++++++++--- .../changelog/273-hs-office-debitor-rbac.sql | 382 ++++++++++-------- 16 files changed, 577 insertions(+), 310 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java index 00d4fab8..1b2045e9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java @@ -16,11 +16,11 @@ import org.hibernate.annotations.GenericGenerator; import jakarta.persistence.*; import java.io.IOException; -import java.util.Optional; import java.util.UUID; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; @@ -108,7 +108,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { public static RbacView rbac() { return rbacViewFor("debitor", HsOfficeDebitorEntity.class) .withIdentityView(SQL.query(""" - SELECT debitor.uuid, + SELECT debitor.uuid AS uuid, 'D-' || (SELECT partner.partnerNumber FROM hs_office_partner partner JOIN hs_office_relationship partnerRel @@ -116,9 +116,10 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { JOIN hs_office_relationship debitorRel ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' WHERE debitorRel.uuid = debitor.debitorRelUuid) - || to_char(debitorNumberSuffix, 'fm00') - from hs_office_debitor as debitor + || to_char(debitorNumberSuffix, 'fm00') as idName + FROM hs_office_debitor AS debitor """)) + .withRestrictedViewOrderBy(SQL.projection("defaultPrefix")) .withUpdatableColumns( "debitorRel", "billable", @@ -129,13 +130,13 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { "vatBusiness", "vatReverseCharge", "defaultPrefix" /* TODO: do we want that updatable? */) - .createPermission(custom("new-debitor")).grantedTo("global", ADMIN) + .toRole("global", ADMIN).grantPermission("debitor", INSERT) .importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class, fetchedBySql(""" SELECT * FROM hs_office_relationship AS r - WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid + WHERE r.relType = 'ACCOUNTING' AND r.uuid = ${REF}.debitorRelUuid """), dependsOnColumn("debitorRelUuid")) .createPermission(DELETE).grantedTo("debitorRel", OWNER) @@ -143,21 +144,27 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { .createPermission(SELECT).grantedTo("debitorRel", TENANT) .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, - dependsOnColumn("refundBankAccountUuid"), fetchedBySql(""" + dependsOnColumn("refundBankAccountUuid"), + fetchedBySql(""" SELECT * FROM hs_office_relationship AS r WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid - """) + """), + NULLABLE ) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) .importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, - dependsOnColumn("partnerRelUuid"), fetchedBySql(""" - SELECT * - FROM hs_office_relationship AS partnerRel - WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid - """) + dependsOnColumn("debitorRelUuid"), + fetchedBySql(""" + SELECT partnerRel.* + FROM hs_office_relationship AS partnerRel + JOIN hs_office_relationship AS debitorRel + ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid + WHERE partnerRel.relType = 'PARTNER' + AND ${REF}.debitorRelUuid = debitorRel.uuid + """) ) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT) @@ -169,6 +176,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { } public static void main(String[] args) throws IOException { - rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated"); + rbac().generateWithBaseFileName("273-hs-office-debitor-rbac"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java index a91f8637..4a41f553 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java @@ -83,7 +83,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable { "birthName", "birthday", "dateOfDeath") - .createPermission(custom("new-partner-details")).grantedTo("global", ADMIN) + .toRole("global", ADMIN).grantPermission("partner-details", INSERT) .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, fetchedBySql(""" diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java index 88c48ebb..4971b769 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java @@ -27,7 +27,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.persistence.*; import java.io.IOException; -import java.util.Optional; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; @@ -105,7 +104,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { "partnerRoleUuid", "personUuid", "contactUuid") - .createPermission(custom("new-partner")).grantedTo("global", ADMIN) + .toRole("global", ADMIN).grantPermission("partner", INSERT) // FIXME: global -> partnerRel.relAnchor? .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"), 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 085a1999..8bdd18ae 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java @@ -55,7 +55,7 @@ public class InsertTriggerGenerator { LOOP roleUuid := findRoleId(${rawSuperRoleDescriptor}); permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}'); - call grantPermissionToRole(roleUuid, permissionUuid); + call grantPermissionToRole(permissionUuid, roleUuid); END LOOP; END; $$; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java index e2c06515..a42bbbe8 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -32,6 +32,7 @@ import java.util.stream.Stream; import static java.lang.reflect.Modifier.isStatic; import static java.util.Arrays.stream; import static java.util.Optional.ofNullable; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched; import static org.apache.commons.lang3.StringUtils.uncapitalize; @@ -141,35 +142,42 @@ public class RbacView { if (rootEntityAliasProxy != null) { throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy); } - rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false); + rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL); return this; } public RbacView importSubEntityAlias( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { - importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true); + importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL); + return this; + } + + public RbacView importEntityAlias( + final String aliasName, final Class entityClass, + final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) { + importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable); return this; } public RbacView importEntityAlias( final String aliasName, final Class entityClass, final Column dependsOnColum, final SQL fetchSql) { - importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false); + importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL); return this; } public RbacView importEntityAlias( final String aliasName, final Class entityClass, final Column dependsOnColum) { - importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false); + importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false, null); return this; } private EntityAlias importEntityAliasImpl( final String aliasName, final Class entityClass, - final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) { - final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity); + final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { + final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable); entityAliases.put(aliasName, entityAlias); try { importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity); @@ -281,6 +289,7 @@ public class RbacView { return RbacView.this; } + // TODO: switch order or parameters for more natural readability public RbacView grantPermission(final String entityAliasName, final Permission perm) { final var entityAlias = findEntityAlias(entityAliasName); final var forTable = entityAlias.getRawTableName(); @@ -290,6 +299,11 @@ public class RbacView { } + public enum Nullable { + NOT_NULL, // DEFAULT + NULLABLE + } + @Getter @EqualsAndHashCode public class RbacGrantDefinition { @@ -560,14 +574,14 @@ public class RbacView { .orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition)); } - record EntityAlias(String aliasName, Class entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) { + record EntityAlias(String aliasName, Class entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) { public EntityAlias(final String aliasName) { - this(aliasName, null, null, null, false); + this(aliasName, null, null, null, false, null); } public EntityAlias(final String aliasName, final Class entityClass) { - this(aliasName, entityClass, null, null, false); + this(aliasName, entityClass, null, null, false, null); } boolean isGlobal() { @@ -646,20 +660,15 @@ public class RbacView { } } - public record Permission(String permission) { - - public static final Permission INSERT = new Permission("INSERT"); - public static final Permission DELETE = new Permission("DELETE"); - public static final Permission UPDATE = new Permission("UPDATE"); - public static final Permission SELECT = new Permission("SELECT"); - - public static Permission custom(final String permission) { - return new Permission(permission); - } + public enum Permission { + INSERT, + DELETE, + UPDATE, + SELECT; @Override public String toString() { - return ":" + permission; + return ":" + name(); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java index eb8f3534..9850d942 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java @@ -37,8 +37,11 @@ public class RbacViewPostgresGenerator { @Override public String toString() { - return plPgSql.toString(); -} + return plPgSql.toString() + .replace("\n\n\n", "\n\n") + .replace("-- ====", "\n-- ====") + .replace("\n\n--//", "\n--//"); + } @SneakyThrows public void generateToChangeLog(final Path outputPath) { 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 edb1f609..d6845901 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -82,6 +82,7 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.writeLn("begin"); plPgSql.indented(() -> { plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);"); + plPgSql.writeLn(); generateCreateRolesAndGrantsAfterInsert(plPgSql); plPgSql.ensureSingleEmptyLine(); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);"); @@ -109,7 +110,7 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.chopEmptyLines(); plPgSql.indented(() -> { - updatableEntityAliases() + referencedEntityAliases() .forEach((ea) -> { plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";"); plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"); @@ -120,6 +121,7 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.writeLn("begin"); plPgSql.indented(() -> { plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);"); + plPgSql.writeLn(); generateUpdateRolesAndGrantsAfterUpdate(plPgSql); plPgSql.ensureSingleEmptyLine(); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);"); @@ -134,9 +136,10 @@ class RolesGrantsAndPermissionsGenerator { private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) { referencedEntityAliases() - .forEach((ea) -> plPgSql.writeLn( - ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";", - with("ref", NEW.name()))); + .forEach((ea) -> { + generateFetchedVars(plPgSql, ea, NEW); + plPgSql.writeLn(); + }); createRolesWithGrantsSql(plPgSql, OWNER); createRolesWithGrantsSql(plPgSql, ADMIN); @@ -165,14 +168,11 @@ class RolesGrantsAndPermissionsGenerator { private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) { plPgSql.ensureSingleEmptyLine(); - updatableEntityAliases() + referencedEntityAliases() .forEach((ea) -> { - plPgSql.writeLn( - ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";", - with("ref", OLD.name())); - plPgSql.writeLn( - ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";", - with("ref", NEW.name())); + generateFetchedVars(plPgSql, ea, OLD); + generateFetchedVars(plPgSql, ea, NEW); + plPgSql.writeLn(); }); updatableEntityAliases() @@ -190,6 +190,23 @@ class RolesGrantsAndPermissionsGenerator { }); } + private void generateFetchedVars( + final StringWriter plPgSql, + final RbacView.EntityAlias ea, + final PostgresTriggerReference old) { + plPgSql.writeLn( + ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";", + with("ref", old.name())); + if (ea.nullable() == RbacView.Nullable.NOT_NULL) { + plPgSql.writeLn( + "assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s', ${REF}.${dependsOnColumn});", + with("entityRefVar", entityRefVar(old, ea)), + with("dependsOnColumn", ea.dependsOnColumName()), + with("ref", old.name())); + plPgSql.writeLn(); + } + } + private boolean isUpdatable(final RbacView.Column c) { return rbacDef.getUpdatableColumns().contains(c); } @@ -256,7 +273,7 @@ class RolesGrantsAndPermissionsGenerator { .replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias) ? ref.name() : refVarName(ref, permDef.entityAlias)) - .replace("${perm}", permDef.permission.permission()); + .replace("${perm}", permDef.permission.name()); } private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) { @@ -333,7 +350,7 @@ class RolesGrantsAndPermissionsGenerator { final var arrayElements = permissionGrantsForRole.stream() .map(RbacView.RbacGrantDefinition::getPermDef) .map(RbacPermissionDefinition::getPermission) - .map(RbacView.Permission::permission) + .map(RbacView.Permission::name) .map(p -> "'" + p + "'") .sorted() .toList(); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java index 512ec72d..3e065c92 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java @@ -103,8 +103,8 @@ public class StringWriter { text = matcher.replaceAll(varDef.value()); }); return text; - } catch (Exception exc) { - throw exc; + } catch (final RuntimeException exc) { + throw exc; // FIXME: just for debugging, remove try/catch before merging to master } } } diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index fd0983bd..26a8410a 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -609,7 +609,7 @@ select exists( ); $$; -create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid) +create or replace procedure grantPermissionToRole(permissionUuid uuid, roleUuid uuid) language plpgsql as $$ begin perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole'); @@ -622,10 +622,10 @@ begin end; $$; -create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid) +create or replace procedure grantPermissionToRole(permissionUuid uuid, roleDesc RbacRoleDescriptor) language plpgsql as $$ begin - call grantPermissionToRole(findRoleId(roleDesc), permissionUuid); + call grantPermissionToRole(permissionUuid, findRoleId(roleDesc)); end; $$; @@ -671,6 +671,11 @@ declare superRoleId uuid; subRoleId uuid; begin + -- FIXME: maybe separate method grantRoleToRoleIfNotNull(...)? + if superRole.objectUuid is null or subRole.objectuuid is null then + return; + end if; + superRoleId := findRoleId(superRole); subRoleId := findRoleId(subRole); 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 20562642..1c320e58 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -160,7 +160,7 @@ do language plpgsql $$ LOOP roleUuid := findRoleId(testCustomerAdmin(row)); permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package'); - call grantPermissionToRole(roleUuid, permissionUuid); + call grantPermissionToRole(permissionUuid, roleUuid); END LOOP; END; $$; @@ -174,8 +174,8 @@ create or replace function test_package_test_customer_insert_tf() strict as $$ begin call grantPermissionToRole( - testCustomerAdmin(NEW), - createPermission(NEW.uuid, 'INSERT', 'test_package')); + createPermission(NEW.uuid, 'INSERT', 'test_package'), + testCustomerAdmin(NEW)); return NEW; end; $$; diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql index e686dada..0fd691e6 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.sql +++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql @@ -159,7 +159,7 @@ do language plpgsql $$ LOOP roleUuid := findRoleId(testPackageAdmin(row)); permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain'); - call grantPermissionToRole(roleUuid, permissionUuid); + call grantPermissionToRole(permissionUuid, roleUuid); END LOOP; END; $$; @@ -173,8 +173,8 @@ create or replace function test_domain_test_package_insert_tf() strict as $$ begin call grantPermissionToRole( - testPackageAdmin(NEW), - createPermission(NEW.uuid, 'INSERT', 'test_domain')); + createPermission(NEW.uuid, 'INSERT', 'test_domain'), + testPackageAdmin(NEW)); return NEW; end; $$; diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql index f83d2dde..a15901da 100644 --- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql @@ -94,7 +94,7 @@ do language plpgsql $$ LOOP roleUuid := findRoleId(globalGuest()); permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person'); - call grantPermissionToRole(roleUuid, permissionUuid); + call grantPermissionToRole(permissionUuid, roleUuid); END LOOP; END; $$; @@ -108,8 +108,8 @@ create or replace function hs_office_person_global_insert_tf() strict as $$ begin call grantPermissionToRole( - globalGuest(), - createPermission(NEW.uuid, 'INSERT', 'hs_office_person')); + createPermission(NEW.uuid, 'INSERT', 'hs_office_person'), + globalGuest()); return NEW; end; $$; diff --git a/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql b/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql index 5c9c0203..add49ccc 100644 --- a/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql +++ b/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql @@ -106,7 +106,7 @@ do language plpgsql $$ call createHsOfficeRelationshipTestData('Third OHG', 'ACCOUNTING', 'Third OHG', 'third contact'); call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact'); - call createHsOfficeRelationshipTestData('Smith', 'ACCOUNTING', 'Smith', 'third contact', 'members-announce'); + call createHsOfficeRelationshipTestData('Smith', 'ACCOUNTING', 'Smith', 'third contact'); call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce'); end; $$; diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql index 74a95ce2..19cf5a75 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql @@ -94,7 +94,7 @@ do language plpgsql $$ LOOP roleUuid := findRoleId(globalGuest()); permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'); - call grantPermissionToRole(roleUuid, permissionUuid); + call grantPermissionToRole(permissionUuid, roleUuid); END LOOP; END; $$; @@ -108,8 +108,8 @@ create or replace function hs_office_bankaccount_global_insert_tf() strict as $$ begin call grantPermissionToRole( - globalGuest(), - createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount')); + createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'), + globalGuest()); return NEW; end; $$; diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md index f0455e2f..20940b35 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md @@ -1,74 +1,275 @@ -### hs_office_debitor RBAC Roles +### rbac debitor + +This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-12T16:22:27.339854728. ```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% flowchart TB -subgraph partnerRelationship[hsOfficeRelationship:PARTNER] +subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"] direction TB - style partnerRelationship fill:#eee - - role:partnerRelationship.owner[relationship.owner] - --> role:partnerRelationship.admin[relationship.admin] - --> role:partnerRelationship.agent[relationship.agent] - --> role:partnerRelationship.tenant[relationship.tenant] + style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px - partnerPersonAdmin>e.g. partnerPerson.admin] --> role:partnerRelationship.agent - otherPersonAdmin>e.g. operationalPerson.admin] --> role:partnerRelationship.tenant - role:partnerRelationship.tenant --> partnerPersonReferrer>e.g. partnerPerson.referrer] + subgraph debitorRel.anchorPerson:roles[ ] + style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]] + role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]] + role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]] + end end -subgraph internal[ ] +subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"] direction TB - style internal fill:#fff + style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px - subgraph refundBankAccount - direction TB - style refundBankAccount fill:#eee - - role:refundBankAccount.owner[bankAccount.owner] - --> role:refundBankAccount.admin[bankAccount.admin] - --> role:refundBankAccount.referrer[bankAccount.referrer] + subgraph debitorRel.holderPerson:roles[ ] + style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]] + role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]] + role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]] end - - - subgraph debitorRelationship[hsOfficeRelationship:DEBITOR] - direction TB - style debitorRelationship fill:#eee - - role:debitorRelationship.owner[relationship.owner] - --> role:debitorRelationship.admin[relationship.admin] - --> role:debitorRelationship.agent[relationship.agent] - --> role:debitorRelationship.tenant[relationship.tenant] - end - - subgraph debitor - direction TB - - role:debitorRelationship.owner[debitorRelationship.owner] - %% permissions - ==> perm:debitor.*{{debitor.*}} - - role:debitorRelationship.admin[debitorRelationship.admin] - %% permissions - ==> perm:debitor.edit{{debitor.edit}} - %% incoming - role:partnerRelationship.admin ==> role:debitorRelationship.admin - %% outgoing - role:debitorRelationship.admin ==> role:partnerRelationship.agent - - role:debitorRelationship.agent[debitorRelationship.agent] - %% incoming - role:partnerRelationship.agent ==> role:debitorRelationship.agent - role:refundBankAccount.admin ==> role:debitorRelationship.agent - %% outgoing - role:debitorRelationship.agent ==> role:partnerRelationship.tenant - role:debitorRelationship.agent ==> role:refundBankAccount.referrer - - role:debitorRelationship.tenant[debitorRelationship.tenant] - ==> perm:debitor.view{{debitor.view}} - - end - end + +subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"] + direction TB + style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.holderPerson:roles[ ] + style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]] + role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]] + role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]] + end +end + +subgraph debitor["`**debitor**`"] + direction TB + style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph debitor:permissions[ ] + style debitor:permissions fill:#dd4901,stroke:white + + perm:debitor:INSERT{{debitor:INSERT}} + perm:debitor:DELETE{{debitor:DELETE}} + perm:debitor:UPDATE{{debitor:UPDATE}} + perm:debitor:SELECT{{debitor:SELECT}} + end + + subgraph debitorRel["`**debitorRel**`"] + direction TB + style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"] + direction TB + style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.anchorPerson:roles[ ] + style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]] + role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]] + role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]] + end + end + + subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"] + direction TB + style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.holderPerson:roles[ ] + style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]] + role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]] + role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]] + end + end + + subgraph debitorRel.contact["`**debitorRel.contact**`"] + direction TB + style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.contact:roles[ ] + style debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:debitorRel.contact:owner[[debitorRel.contact:owner]] + role:debitorRel.contact:admin[[debitorRel.contact:admin]] + role:debitorRel.contact:referrer[[debitorRel.contact:referrer]] + end + end + + subgraph debitorRel:roles[ ] + style debitorRel:roles fill:#99bcdb,stroke:white + + role:debitorRel:owner[[debitorRel:owner]] + role:debitorRel:admin[[debitorRel:admin]] + role:debitorRel:agent[[debitorRel:agent]] + role:debitorRel:tenant[[debitorRel:tenant]] + end + end +end + +subgraph partnerRel["`**partnerRel**`"] + direction TB + style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"] + direction TB + style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.holderPerson:roles[ ] + style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]] + role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]] + role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]] + end + end + + subgraph partnerRel.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.contact:roles[ ] + style partnerRel.contact:roles fill:#99bcdb,stroke:white + + role:partnerRel.contact:owner[[partnerRel.contact:owner]] + role:partnerRel.contact:admin[[partnerRel.contact:admin]] + role:partnerRel.contact:referrer[[partnerRel.contact:referrer]] + end + end + + subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"] + direction TB + style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.anchorPerson:roles[ ] + style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]] + role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]] + role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]] + end + end + + subgraph partnerRel:roles[ ] + style partnerRel:roles fill:#99bcdb,stroke:white + + role:partnerRel:owner[[partnerRel:owner]] + role:partnerRel:admin[[partnerRel:admin]] + role:partnerRel:agent[[partnerRel:agent]] + role:partnerRel:tenant[[partnerRel:tenant]] + end +end + +subgraph partnerRel.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.contact:roles[ ] + style partnerRel.contact:roles fill:#99bcdb,stroke:white + + role:partnerRel.contact:owner[[partnerRel.contact:owner]] + role:partnerRel.contact:admin[[partnerRel.contact:admin]] + role:partnerRel.contact:referrer[[partnerRel.contact:referrer]] + end +end + +subgraph debitorRel.contact["`**debitorRel.contact**`"] + direction TB + style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.contact:roles[ ] + style debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:debitorRel.contact:owner[[debitorRel.contact:owner]] + role:debitorRel.contact:admin[[debitorRel.contact:admin]] + role:debitorRel.contact:referrer[[debitorRel.contact:referrer]] + end +end + +subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"] + direction TB + style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph partnerRel.anchorPerson:roles[ ] + style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]] + role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]] + role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]] + end +end + +subgraph refundBankAccount["`**refundBankAccount**`"] + direction TB + style refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph refundBankAccount:roles[ ] + style refundBankAccount:roles fill:#99bcdb,stroke:white + + role:refundBankAccount:owner[[refundBankAccount:owner]] + role:refundBankAccount:admin[[refundBankAccount:admin]] + role:refundBankAccount:referrer[[refundBankAccount:referrer]] + end +end + +%% granting roles to roles +role:global:admin -.-> role:debitorRel.anchorPerson:owner +role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin +role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer +role:global:admin -.-> role:debitorRel.holderPerson:owner +role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin +role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer +role:global:admin -.-> role:debitorRel.contact:owner +role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin +role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer +role:global:admin -.-> role:debitorRel:owner +role:debitorRel:owner -.-> role:debitorRel:admin +role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin +role:debitorRel:admin -.-> role:debitorRel:agent +role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent +role:debitorRel:agent -.-> role:debitorRel:tenant +role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant +role:debitorRel.contact:admin -.-> role:debitorRel:tenant +role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer +role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer +role:debitorRel:tenant -.-> role:debitorRel.contact:referrer +role:global:admin -.-> role:refundBankAccount:owner +role:refundBankAccount:owner -.-> role:refundBankAccount:admin +role:refundBankAccount:admin -.-> role:refundBankAccount:referrer +role:refundBankAccount:admin ==> role:debitorRel:agent +role:debitorRel:agent ==> role:refundBankAccount:referrer +role:global:admin -.-> role:partnerRel.anchorPerson:owner +role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin +role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer +role:global:admin -.-> role:partnerRel.holderPerson:owner +role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin +role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer +role:global:admin -.-> role:partnerRel.contact:owner +role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin +role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer +role:global:admin -.-> role:partnerRel:owner +role:partnerRel:owner -.-> role:partnerRel:admin +role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin +role:partnerRel:admin -.-> role:partnerRel:agent +role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent +role:partnerRel:agent -.-> role:partnerRel:tenant +role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant +role:partnerRel.contact:admin -.-> role:partnerRel:tenant +role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer +role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer +role:partnerRel:tenant -.-> role:partnerRel.contact:referrer +role:partnerRel:admin ==> role:debitorRel:admin +role:partnerRel:agent ==> role:debitorRel:agent +role:debitorRel:agent ==> role:partnerRel:tenant + +%% granting permissions to roles +role:global:admin ==> perm:debitor:INSERT +role:debitorRel:owner ==> perm:debitor:DELETE +role:debitorRel:admin ==> perm:debitor:UPDATE +role:debitorRel:tenant ==> perm:debitor:SELECT + ``` - diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index fe11d8bd..9d05ff1f 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -1,4 +1,6 @@ --liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator at 2024-03-12T16:22:27.348469700. + -- ============================================================================ --changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--// @@ -15,249 +17,273 @@ call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor'); -- ============================================================================ ---changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--// +--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* - Creates and updates the roles and their assignments for debitor entities. + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. */ -create or replace function hsOfficeDebitorRbacRolesTrigger() - returns trigger - language plpgsql - strict as $$ +create or replace procedure buildRbacSystemForHsOfficeDebitor( + NEW hs_office_debitor +) + language plpgsql as $$ + declare - debitorUuid uuid; - - oldDebitorRel hs_office_relationship; - newDebitorRel hs_office_relationship; - - newPartnerRel hs_office_relationship; - - newBankAccount hs_office_bankaccount; - oldBankAccount hs_office_bankaccount; + newPartnerRel hs_office_relationship; + newDebitorRel hs_office_relationship; + newRefundBankAccount hs_office_bankaccount; begin call enterTriggerForObjectUuid(NEW.uuid); - debitorUuid := NEW.uuid; + SELECT partnerRel.* + FROM hs_office_relationship AS partnerRel + JOIN hs_office_relationship AS debitorRel + ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid + WHERE partnerRel.relType = 'PARTNER' + AND NEW.debitorRelUuid = debitorRel.uuid + INTO newPartnerRel; + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); - select * into newDebitorRel - from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid; + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid + INTO newDebitorRel; + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); - select * into newPartnerRel - from hs_office_relationship as partnerRel - where newDebitorRel.relAnchorUuid = partnerRel.relHolderUuid; + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid + INTO newRefundBankAccount; - select * into newBankAccount - from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid; + call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel)); + call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel)); + call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount)); + call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel)); + call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel)); - if TG_OP = 'INSERT' then + call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationshipOwner(newDebitorRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationshipTenant(newDebitorRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationshipAdmin(newDebitorRel)); - -- Permissions and Grants for Debitor + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; - call grantPermissionsToRole( - getRoleId(hsOfficeRelationshipOwner(newDebitorRel)), - createPermissions(partnerUuid, array ['DELETE']) - ); +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row. + */ - call grantPermissionsToRole( - getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'), - createPermissions(partnerUuid, array ['UPDATE']) - ); +create or replace function insertTriggerForHsOfficeDebitor_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficeDebitor(NEW); + return NEW; +end; $$; - call grantPermissionsToRole( - getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'), - createPermissions(partnerUuid, array ['SELECT']) - ); +create trigger insertTriggerForHsOfficeDebitor_tg + after insert on hs_office_debitor + for each row +execute procedure insertTriggerForHsOfficeDebitor_tf(); +--// - -- Grants to and from related Partner Relationship --- call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true); --- call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true); --- --- call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel), true); --- call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel), true); +-- ============================================================================ +--changeset hs-office-debitor-rbac-update-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- - -- Grants to and from refundBankAccount +/* + Called from the AFTER UPDATE TRIGGER to re-wire the grants. + */ --- if newBankAccount is not null then --- call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true); --- call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true); --- end if; +create or replace procedure updateRbacRulesForHsOfficeDebitor( + OLD hs_office_debitor, + NEW hs_office_debitor +) + language plpgsql as $$ - elsif TG_OP = 'UPDATE' then +declare + oldPartnerRel hs_office_relationship; + newPartnerRel hs_office_relationship; + oldDebitorRel hs_office_relationship; + newDebitorRel hs_office_relationship; + oldRefundBankAccount hs_office_bankaccount; + newRefundBankAccount hs_office_bankaccount; - if OLD.debitorRelUuid is distinct from NEW.debitorRelUuid then +begin + call enterTriggerForObjectUuid(NEW.uuid); - select * into oldDebitorRel - from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid; + SELECT partnerRel.* + FROM hs_office_relationship AS partnerRel + JOIN hs_office_relationship AS debitorRel + ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid + WHERE partnerRel.relType = 'PARTNER' + AND OLD.debitorRelUuid = debitorRel.uuid + INTO oldPartnerRel; + assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid); --- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)), --- createPermissions(partnerUuid, array ['DELETE']) --- ); --- --- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel)), --- createPermissions(partnerUuid, array ['UPDATE']) --- ); --- --- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipTenant(newDebitorRel)), --- createPermissions(partnerUuid, array ['SELECT']) --- ); + SELECT partnerRel.* + FROM hs_office_relationship AS partnerRel + JOIN hs_office_relationship AS debitorRel + ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid + WHERE partnerRel.relType = 'PARTNER' + AND NEW.debitorRelUuid = debitorRel.uuid + INTO newPartnerRel; + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); - end if; + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.uuid = OLD.debitorRelUuid + INTO oldDebitorRel; + assert oldDebitorRel.uuid is not null, format('oldDebitorRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid); - if OLD.refundBankAccountUuid is distinct from NEW.refundBankAccountUuid then + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid + INTO newDebitorRel; + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); - select * into oldBankAccount - from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid; + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = OLD.debitorRelUuid + INTO oldRefundBankAccount; + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid + INTO newRefundBankAccount; - if oldBankAccount is not null then - call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldBankAccount), hsOfficeRelationshipAgent(oldDebitorRel), true); - call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldBankAccount), true); - end if; + if NEW.refundBankAccountUuid <> OLD.refundBankAccountUuid then + + call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldRefundBankAccount)); + call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount)); + + call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldRefundBankAccount), hsOfficeRelationshipAgent(oldDebitorRel)); + call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel)); - if newBankAccount is not null then - call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true); - call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true); - end if; - end if; - else - raise exception 'invalid usage of TRIGGER'; end if; call leaveTriggerForObjectUuid(NEW.uuid); - return NEW; end; $$; /* - An AFTER INSERT TRIGGER which creates the role structure for a new debitor. - */ -create trigger createRbacRolesForHsOfficeDebitor_Trigger - after insert - on hs_office_debitor - for each row -execute procedure hsOfficeDebitorRbacRolesTrigger(); - -/* - An AFTER UPDATE TRIGGER which updates the role structure of a debitor. - */ -create trigger updateRbacRolesForHsOfficeDebitor_Trigger - after update - on hs_office_debitor - for each row -execute procedure hsOfficeDebitorRbacRolesTrigger(); ---// - - -/* - Creates and updates the roles and their assignments for debitor entities if partner rel changes. + AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row. */ -create or replace function hsOfficeDebitorPartnerRelRbacRolesTrigger() +create or replace function updateTriggerForHsOfficeDebitor_tf() returns trigger language plpgsql strict as $$ -declare - begin - call enterTriggerForObjectUuid(NEW.uuid); - - -- TODO - - call leaveTriggerForObjectUuid(NEW.uuid); + call updateRbacRulesForHsOfficeDebitor(OLD, NEW); return NEW; end; $$; + +create trigger updateTriggerForHsOfficeDebitor_tg + after update on hs_office_debitor + for each row +execute procedure updateTriggerForHsOfficeDebitor_tf(); --// + +-- ============================================================================ +--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + /* - An AFTER UPDATE TRIGGER which creates the role structure for debitors if partner relations change. + Creates INSERT INTO hs_office_debitor permissions for the related global rows. */ -create trigger updateRbacRolesForHsOfficeDebitor_Trigger - after update - on hs_office_partner - for each row -execute procedure hsOfficeDebitorPartnerRelRbacRolesTrigger(); ---// +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows'); + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalAdmin()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_debitor INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_debitor_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + globalAdmin(), + createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor')); + return NEW; +end; $$; + +create trigger hs_office_debitor_global_insert_tg + after insert on global + for each row +execute procedure hs_office_debitor_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor. +*/ +create or replace function hs_office_debitor_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_debitor not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_debitor_insert_permission_check_tg + before insert on hs_office_debitor + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_debitor_insert_permission_missing_tf(); +--// -- ============================================================================ --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_debitor', $idName$ - '#' || (select partner.partnerNumber - from hs_office_partner partner - join hs_office_relationship partnerRel on partnerRel.uuid = partner.partnerRoleUUid and partnerRel.relType = 'PARTNER' - join hs_office_relationship debitorRel on debitorRel.relAnchorUuid = partnerRel.relHolderUuid and partnerRel.relType = 'ACCOUNTING' - where debitorRel.uuid = target.debitorRelUuid) - || to_char(debitorNumberSuffix, 'fm00') - || ':' || (select split_part(idName, ':', 2) from hs_office_relationship_iv ri where ri.uuid = target.debitorRelUuid) - $idName$); ---// + call generateRbacIdentityViewFromQuery('hs_office_debitor', $idName$ + SELECT debitor.uuid AS uuid, + 'D-' || (SELECT partner.partnerNumber + FROM hs_office_partner partner + JOIN hs_office_relationship partnerRel + ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER' + JOIN hs_office_relationship debitorRel + ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' + WHERE debitorRel.uuid = debitor.debitorRelUuid) + || to_char(debitorNumberSuffix, 'fm00') as idName + FROM hs_office_debitor AS debitor + + $idName$); +--// -- ============================================================================ --changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix', +call generateRbacRestrictedView('hs_office_debitor', + $orderBy$ + defaultPrefix + $orderBy$, $updates$ - debitorRel = new.debitorRel, + debitorRel = new.debitorRel, billable = new.billable, - billingContactUuid = new.billingContactUuid, + debitorUuid = new.debitorUuid, refundBankAccountUuid = new.refundBankAccountUuid, vatId = new.vatId, vatCountryCode = new.vatCountryCode, vatBusiness = new.vatBusiness, - vatreversecharge = new.vatreversecharge, - defaultPrefix = new.defaultPrefix -- TODO: Should it be allowed to updated this value? + vatReverseCharge = new.vatReverseCharge, + defaultPrefix = new.defaultPrefix $updates$); --// --- ============================================================================ ---changeset hs-office-debitor-rbac-NEW-DEBITOR:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -/* - Creates a global permission for new-debitor and assigns it to the hostsharing admins role. - */ -do language plpgsql $$ - declare - addDebitorPermissions uuid[]; - globalObjectUuid uuid; - globalAdminRoleUuid uuid ; - begin - call defineContext('granting global new-debitor permission to global admin role', null, null, null); - - globalAdminRoleUuid := findRoleId(globalAdmin()); - globalObjectUuid := (select uuid from global); - addDebitorPermissions := createPermissions(globalObjectUuid, array ['new-debitor']); - call grantPermissionsToRole(globalAdminRoleUuid, addDebitorPermissions); - end; -$$; - -/** - Used by the trigger to prevent the add-debitor to current user respectively assumed roles. - */ -create or replace function addHsOfficeDebitorNotAllowedForCurrentSubjects() - returns trigger - language PLPGSQL -as $$ -begin - raise exception '[403] new-debitor not permitted for %', - array_to_string(currentSubjects(), ';', 'null'); -end; $$; - -/** - Checks if the user or assumed roles are allowed to create a new debitor. - */ -create trigger hs_office_debitor_insert_trigger - before insert - on hs_office_debitor - for each row - -- TODO.spec: who is allowed to create new debitors - when ( not hasAssumedRole() ) -execute procedure addHsOfficeDebitorNotAllowedForCurrentSubjects(); ---// -