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 a90f3138..15ad081f 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 @@ -150,11 +150,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { .toRole("global", ADMIN).grantPermission(INSERT) .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, - fetchedBySql(""" - SELECT * - FROM hs_office_relation AS r - WHERE r.type = 'DEBITOR' AND r.uuid = ${REF}.debitorRelUuid - """), + directlyFetchedByDependsOnColumn(), dependsOnColumn("debitorRelUuid")) .createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(UPDATE).grantedTo("debitorRel", ADMIN) @@ -170,7 +166,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { .importEntityAlias("partnerRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorRelUuid"), fetchedBySql(""" - SELECT partnerRel.* + SELECT ${columns} FROM hs_office_relation AS partnerRel JOIN hs_office_relation AS debitorRel ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java index dd7912cb..cc8972b3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java @@ -132,10 +132,10 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { .importEntityAlias("partnerRel", HsOfficeRelationEntity.class, dependsOnColumn("partnerUuid"), fetchedBySql(""" - SELECT r.* - FROM hs_office_partner AS p - JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid - WHERE p.uuid = ${REF}.partnerUuid + SELECT ${columns} + FROM hs_office_partner AS partner + JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid + WHERE partner.uuid = ${REF}.partnerUuid """), NOT_NULL) .toRole("partnerRel", ADMIN).grantPermission(INSERT) 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 1a54c9f0..be550b70 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 @@ -32,7 +32,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnCo import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @@ -98,14 +98,14 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { .toRole("global", ADMIN).grantPermission(INSERT) // FIXME: global -> partnerRel.anchor? .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, - fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"), + directlyFetchedByDependsOnColumn(), dependsOnColumn("partnerRelUuid")) .createPermission(DELETE).grantedTo("partnerRel", ADMIN) .createPermission(UPDATE).grantedTo("partnerRel", AGENT) .createPermission(SELECT).grantedTo("partnerRel", TENANT) .importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class, - fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"), + directlyFetchedByDependsOnColumn(), dependsOnColumn("detailsUuid")) .createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN) .createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java index f4b02875..5301983f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java @@ -20,7 +20,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @@ -91,15 +91,15 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable { .withUpdatableColumns("contactUuid") .importEntityAlias("anchorPerson", HsOfficePersonEntity.class, dependsOnColumn("anchorUuid"), - fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid"), + directlyFetchedByDependsOnColumn(), NOT_NULL) .importEntityAlias("holderPerson", HsOfficePersonEntity.class, dependsOnColumn("holderUuid"), - fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid"), + directlyFetchedByDependsOnColumn(), NOT_NULL) .importEntityAlias("contact", HsOfficeContactEntity.class, dependsOnColumn("contactUuid"), - fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"), + directlyFetchedByDependsOnColumn(), NOT_NULL) .createRole(OWNER, (with) -> { with.owningUser(CREATOR); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index bcfef919..897f89b8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -107,7 +107,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid { .importEntityAlias("debitorRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorUuid"), fetchedBySql(""" - SELECT debitorRel.* + SELECT ${columns} FROM hs_office_relation debitorRel JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = ${REF}.debitorUuid 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 000988fa..469a5d4c 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java @@ -114,8 +114,7 @@ public class InsertTriggerGenerator { } } } else { - final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias(); - if (superRoleEntityAlias.fetchSql().part == RbacView.SQL.Part.AUTO_FETCH) { + if (g.getSuperRoleDef().getEntityAlias().isFetchedByDirectForeignKey()) { generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); } else { generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g); @@ -164,24 +163,27 @@ public class InsertTriggerGenerator { An indirect role is a role FIXME. */ - create or replace function ${rawSubTable}_insert_permission_missing_tf() + create or replace function ${rawSubTable}_insert_permission_check_tf() returns trigger language plpgsql as $$ + + declare + superRoleObjectUuid uuid; + begin - if ( not hasInsertPermission( - ( SELECT ${varName}.uuid FROM """, - with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()), - with("varName", g.getSuperRoleDef().getEntityAlias().aliasName())); - plPgSql.indented(3, () -> { + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); + plPgSql.chopEmptyLines(); + plPgSql.indented(2, () -> { plPgSql.writeLn( - "(" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ") AS ${varName}", - with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()), + "superRoleObjectUuid := (" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ");\n" + + "assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';", + with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"), with("ref", NEW.name())); }); + plPgSql.writeLn(); plPgSql.writeLn(""" - - ), 'INSERT', '${rawSubTable}') ) then + if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', '${rawSubTable}') ) then raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)', currentSubjects(), currentSubjectsUuids(); @@ -192,7 +194,7 @@ public class InsertTriggerGenerator { create trigger ${rawSubTable}_insert_permission_check_tg before insert on ${rawSubTable} for each row - execute procedure ${rawSubTable}_insert_permission_missing_tf(); + execute procedure ${rawSubTable}_insert_permission_check_tf(); """, with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java index 7e3c6a3b..066acba2 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java @@ -26,18 +26,20 @@ public class RbacIdentityViewGenerator { plPgSql.writeLn( switch (rbacDef.getIdentityViewSqlQuery().part) { case SQL_PROJECTION -> """ - call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$ - ${identityViewSqlPart} + call generateRbacIdentityViewFromProjection('${rawTableName}', + $idName$ + ${identityViewSqlPart} $idName$); """; case SQL_QUERY -> """ - call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$ - ${identityViewSqlPart} + call generateRbacIdentityViewFromQuery('${rawTableName}', + $idName$ + ${identityViewSqlPart} $idName$); """; default -> throw new IllegalStateException("illegal SQL part given"); }, - with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql), + with("identityViewSqlPart", StringWriter.indented(2, rbacDef.getIdentityViewSqlQuery().sql)), with("rawTableName", rawTableName)); plPgSql.writeLn("--//"); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java index f0bd50a0..b5757865 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java @@ -32,10 +32,10 @@ public class RbacRestrictedViewGenerator { """, with("liquibaseTagPrefix", liquibaseTagPrefix), - with("orderBy", indented(rbacDef.getOrderBySqlExpression().sql, 2)), - with("updates", indented(rbacDef.getUpdatableColumns().stream() + with("orderBy", indented(2, rbacDef.getOrderBySqlExpression().sql)), + with("updates", indented(2, rbacDef.getUpdatableColumns().stream() .map(c -> c + " = new." + c) - .collect(joining(",\n")), 2)), + .collect(joining(",\n")))), with("rawTableName", rawTableName)); } } 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 99acade9..d6fe2ab3 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -34,6 +34,7 @@ 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.Part.AUTO_FETCH; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static org.apache.commons.lang3.StringUtils.uncapitalize; @@ -839,8 +840,8 @@ public class RbacView { }; } - public boolean hasFetchSql() { - return fetchSql != null; + boolean isFetchedByDirectForeignKey() { + return fetchSql != null && fetchSql.part == AUTO_FETCH; } private String withoutEntitySuffix(final String simpleEntityName) { @@ -909,14 +910,25 @@ public class RbacView { /** * DSL method to specify an SQL SELECT expression which fetches the related entity, - * using the reference `${ref}` of the root entity. - * `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function. - * `into ...` will be added with a variable name prefixed with either `new` or `old`. + * using the reference `${ref}` of the root entity and `${columns}` for the projection. + * + *

The query must define the entity alias name of the fetched table + * as its alias for, so it can be used in the generated projection (the columns between + * `SELECT` and `FROM`.

+ * + *

`${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function. + * `into ...` will be added with a variable name prefixed with either `new` or `old`.

+ * + *

`${columns}` is going to be replaced by the columns which are needed for the query, + * e.g. `*` or `uuid`.

* * @param sql an SQL SELECT expression (not ending with ';) * @return the wrapped SQL expression */ public static SQL fetchedBySql(final String sql) { + if ( !sql.startsWith("SELECT ${columns}") ) { + throw new IllegalArgumentException("SQL SELECT expression must start with 'SELECT ${columns}', but is: " + sql); + } validateExpression(sql); return new SQL(sql, Part.SQL_QUERY); } @@ -929,7 +941,7 @@ public class RbacView { * @return the wrapped SQL definition object */ public static SQL directlyFetchedByDependsOnColumn() { - return new SQL(null, Part.AUTO_FETCH); + return new SQL(null, AUTO_FETCH); } /** 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 87a730a3..c71418db 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -233,6 +233,7 @@ class RolesGrantsAndPermissionsGenerator { final PostgresTriggerReference old) { plPgSql.writeLn( ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";", + with("columns", ea.aliasName() + ".*"), with("ref", old.name())); if (ea.nullable() == RbacView.Nullable.NOT_NULL) { plPgSql.writeLn( 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 0376236c..fe4b0548 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java @@ -82,7 +82,7 @@ public class StringWriter { return string.toString(); } - public static String indented(final String text, final int indentLevel) { + public static String indented(final int indentLevel, final String text) { final var indentation = StringUtils.repeat(" ", indentLevel); final var indented = stream(text.split("\n")) .map(line -> line.trim().isBlank() ? "" : indentation + line) @@ -94,7 +94,7 @@ public class StringWriter { if ( indentLevel == 0) { return text; } - return indented(text, indentLevel); + return indented(indentLevel, text); } record VarDef(String name, String value){} 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 b98fc949..874cbc9a 100644 --- a/src/main/resources/db/changelog/113-test-customer-rbac.sql +++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql @@ -143,8 +143,9 @@ create trigger test_customer_insert_permission_check_tg --changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('test_customer', $idName$ - prefix +call generateRbacIdentityViewFromProjection('test_customer', + $idName$ + prefix $idName$); --// 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 dc4d042f..6b8e2c80 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -189,30 +189,22 @@ execute procedure test_package_test_customer_insert_tf(); /** Checks if the user or assumed roles are allowed to insert a row to test_package, - where the check is performed by an indirect role. + where the check is performed by a direct role. - An indirect role is a role FIXME. + A direct role is a role depending on a foreign key directly available in the NEW row. */ create or replace function test_package_insert_permission_missing_tf() returns trigger language plpgsql as $$ begin - if ( not hasInsertPermission( - ( SELECT customer.uuid FROM - - (SELECT * FROM test_customer WHERE uuid = NEW.customerUuid) AS customer - - ), 'INSERT', 'test_package') ) then - raise exception - '[403] insert into test_package not allowed for current subjects % (%)', - currentSubjects(), currentSubjectsUuids(); - end if; - return NEW; + raise exception '[403] insert into test_package not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); end; $$; create trigger test_package_insert_permission_check_tg before insert on test_package for each row + when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') ) execute procedure test_package_insert_permission_missing_tf(); --// @@ -220,8 +212,9 @@ create trigger test_package_insert_permission_check_tg --changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('test_package', $idName$ - name +call generateRbacIdentityViewFromProjection('test_package', + $idName$ + name $idName$); --// 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 e87adb9c..63f1391d 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.sql +++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql @@ -188,30 +188,22 @@ execute procedure test_domain_test_package_insert_tf(); /** Checks if the user or assumed roles are allowed to insert a row to test_domain, - where the check is performed by an indirect role. + where the check is performed by a direct role. - An indirect role is a role FIXME. + A direct role is a role depending on a foreign key directly available in the NEW row. */ create or replace function test_domain_insert_permission_missing_tf() returns trigger language plpgsql as $$ begin - if ( not hasInsertPermission( - ( SELECT package.uuid FROM - - (SELECT * FROM test_package WHERE uuid = NEW.packageUuid) AS package - - ), 'INSERT', 'test_domain') ) then - raise exception - '[403] insert into test_domain not allowed for current subjects % (%)', - currentSubjects(), currentSubjectsUuids(); - end if; - return NEW; + raise exception '[403] insert into test_domain not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); end; $$; create trigger test_domain_insert_permission_check_tg before insert on test_domain for each row + when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') ) execute procedure test_domain_insert_permission_missing_tf(); --// @@ -219,8 +211,9 @@ create trigger test_domain_insert_permission_check_tg --changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('test_domain', $idName$ - name +call generateRbacIdentityViewFromProjection('test_domain', + $idName$ + name $idName$); --// diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql index 51c244a8..84437545 100644 --- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql @@ -125,8 +125,9 @@ execute procedure hs_office_contact_global_insert_tf(); --changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$ - label +call generateRbacIdentityViewFromProjection('hs_office_contact', + $idName$ + label $idName$); --// 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 d1cbf39b..94a33ee3 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 @@ -125,8 +125,9 @@ execute procedure hs_office_person_global_insert_tf(); --changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_person', $idName$ - concat(tradeName, familyName, givenName) +call generateRbacIdentityViewFromProjection('hs_office_person', + $idName$ + concat(tradeName, familyName, givenName) $idName$); --// diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql index 5f0df8d0..ec831467 100644 --- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql @@ -37,13 +37,13 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); - select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson; + SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson; assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); - select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson; + SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); - select * from hs_office_contact as c where c.uuid = NEW.contactUuid INTO newContact; + SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact; assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); @@ -73,13 +73,13 @@ begin hsOfficeRelationTenant(NEW), permissions => array['SELECT'], incomingSuperRoles => array[ + hsOfficeContactAdmin(newContact), hsOfficeRelationAgent(NEW), - hsOfficePersonAdmin(newHolderPerson), - hsOfficeContactAdmin(newContact)], + hsOfficePersonAdmin(newHolderPerson)], outgoingSubRoles => array[ hsOfficePersonReferrer(newAnchorPerson), - hsOfficeContactReferrer(newContact), - hsOfficePersonReferrer(newHolderPerson)] + hsOfficePersonReferrer(newHolderPerson), + hsOfficeContactReferrer(newContact)] ); call leaveTriggerForObjectUuid(NEW.uuid); @@ -130,22 +130,22 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); - select * from hs_office_person as p where p.uuid = OLD.holderUuid INTO oldHolderPerson; + SELECT * FROM hs_office_person WHERE uuid = OLD.holderUuid INTO oldHolderPerson; assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid); - select * from hs_office_person as p where p.uuid = NEW.holderUuid INTO newHolderPerson; + SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson; assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); - select * from hs_office_person as p where p.uuid = OLD.anchorUuid INTO oldAnchorPerson; + SELECT * FROM hs_office_person WHERE uuid = OLD.anchorUuid INTO oldAnchorPerson; assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid); - select * from hs_office_person as p where p.uuid = NEW.anchorUuid INTO newAnchorPerson; + SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); - select * from hs_office_contact as c where c.uuid = OLD.contactUuid INTO oldContact; + SELECT * FROM hs_office_contact WHERE uuid = OLD.contactUuid INTO oldContact; assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid); - select * from hs_office_contact as c where c.uuid = NEW.contactUuid INTO newContact; + SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact; assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); @@ -228,30 +228,22 @@ execute procedure hs_office_relation_hs_office_person_insert_tf(); /** Checks if the user or assumed roles are allowed to insert a row to hs_office_relation, - where the check is performed by an indirect role. + where the check is performed by a direct role. - An indirect role is a role FIXME. + A direct role is a role depending on a foreign key directly available in the NEW row. */ create or replace function hs_office_relation_insert_permission_missing_tf() returns trigger language plpgsql as $$ begin - if ( not hasInsertPermission( - ( SELECT anchorPerson.uuid FROM - - (select * from hs_office_person as p where p.uuid = NEW.anchorUuid) AS anchorPerson - - ), 'INSERT', 'hs_office_relation') ) then - raise exception - '[403] insert into hs_office_relation not allowed for current subjects % (%)', - currentSubjects(), currentSubjectsUuids(); - end if; - return NEW; + raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); end; $$; create trigger hs_office_relation_insert_permission_check_tg before insert on hs_office_relation for each row + when ( not hasInsertPermission(NEW.anchorUuid, 'INSERT', 'hs_office_relation') ) execute procedure hs_office_relation_insert_permission_missing_tf(); --// @@ -259,11 +251,11 @@ create trigger hs_office_relation_insert_permission_check_tg --changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$ - (select idName from hs_office_person_iv p where p.uuid = anchorUuid) - || '-with-' || target.type || '-' - || (select idName from hs_office_person_iv p where p.uuid = holderUuid) - +call generateRbacIdentityViewFromProjection('hs_office_relation', + $idName$ + (select idName from hs_office_person_iv p where p.uuid = anchorUuid) + || '-with-' || target.type || '-' + || (select idName from hs_office_person_iv p where p.uuid = holderUuid) $idName$); --// diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql index 33a5c07b..19dd608d 100644 --- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql @@ -36,10 +36,10 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); - SELECT * FROM hs_office_relation AS r WHERE r.uuid = NEW.partnerRelUuid INTO newPartnerRel; + SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); - SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = NEW.detailsUuid INTO newPartnerDetails; + SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel)); @@ -95,16 +95,16 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); - SELECT * FROM hs_office_relation AS r WHERE r.uuid = OLD.partnerRelUuid INTO oldPartnerRel; + SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel; assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid); - SELECT * FROM hs_office_relation AS r WHERE r.uuid = NEW.partnerRelUuid INTO newPartnerRel; + SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); - SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = OLD.detailsUuid INTO oldPartnerDetails; + SELECT * FROM hs_office_partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails; assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid); - SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = NEW.detailsUuid INTO newPartnerDetails; + SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); @@ -220,8 +220,9 @@ create trigger hs_office_partner_insert_permission_check_tg --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$ - 'P-' || partnerNumber +call generateRbacIdentityViewFromProjection('hs_office_partner', + $idName$ + 'P-' || partnerNumber $idName$); --// diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql index 40ba9b80..09656c68 100644 --- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql +++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql @@ -124,12 +124,12 @@ create trigger hs_office_partner_details_insert_permission_check_tg --changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- - call generateRbacIdentityViewFromQuery('hs_office_partner_details', $idName$ - SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName - FROM hs_office_partner_details AS partnerDetails - JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid - JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid - + call generateRbacIdentityViewFromQuery('hs_office_partner_details', + $idName$ + SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as idName + FROM hs_office_partner_details AS partnerDetails + JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid + JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid $idName$); --// 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 c8ba461d..d3952722 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 @@ -125,8 +125,9 @@ execute procedure hs_office_bankaccount_global_insert_tf(); --changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$ - iban +call generateRbacIdentityViewFromProjection('hs_office_bankaccount', + $idName$ + iban $idName$); --// diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql index c8dd1621..b20d786b 100644 --- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql @@ -64,8 +64,8 @@ begin hsOfficeSepaMandateAgent(NEW), incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)], outgoingSubRoles => array[ - hsOfficeBankAccountReferrer(newBankAccount), - hsOfficeRelationAgent(newDebitorRel)] + hsOfficeRelationAgent(newDebitorRel), + hsOfficeBankAccountReferrer(newBankAccount)] ); perform createRoleWithGrants( @@ -151,20 +151,22 @@ execute procedure hs_office_sepamandate_hs_office_relation_insert_tf(); An indirect role is a role FIXME. */ -create or replace function hs_office_sepamandate_insert_permission_missing_tf() +create or replace function hs_office_sepamandate_insert_permission_check_tf() returns trigger language plpgsql as $$ + +declare + superRoleObjectUuid uuid; + begin - if ( not hasInsertPermission( - ( SELECT debitorRel.uuid FROM + superRoleObjectUuid := (SELECT debitorRel.uuid + FROM hs_office_relation debitorRel + JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid + WHERE debitor.uuid = NEW.debitorUuid + ); + assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null'; - (SELECT debitorRel.* - FROM hs_office_relation debitorRel - JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid - WHERE debitor.uuid = NEW.debitorUuid - ) AS debitorRel - - ), 'INSERT', 'hs_office_sepamandate') ) then + if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_sepamandate') ) then raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)', currentSubjects(), currentSubjectsUuids(); @@ -175,18 +177,18 @@ end; $$; create trigger hs_office_sepamandate_insert_permission_check_tg before insert on hs_office_sepamandate for each row - execute procedure hs_office_sepamandate_insert_permission_missing_tf(); + execute procedure hs_office_sepamandate_insert_permission_check_tf(); --// -- ============================================================================ --changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- - call generateRbacIdentityViewFromQuery('hs_office_sepamandate', $idName$ - select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName - from hs_office_sepamandate sm - join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid - + call generateRbacIdentityViewFromQuery('hs_office_sepamandate', + $idName$ + select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName + from hs_office_sepamandate sm + join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid $idName$); --// 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 da861e5d..a29b1ddf 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 @@ -46,10 +46,7 @@ begin INTO newPartnerRel; assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); - SELECT * - FROM hs_office_relation AS r - WHERE r.type = 'DEBITOR' AND r.uuid = NEW.debitorRelUuid - INTO newDebitorRel; + SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount; @@ -196,18 +193,18 @@ create trigger hs_office_debitor_insert_permission_check_tg --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- - call generateRbacIdentityViewFromQuery('hs_office_debitor', $idName$ - SELECT debitor.uuid AS uuid, - 'D-' || (SELECT partner.partnerNumber - FROM hs_office_partner partner - JOIN hs_office_relation partnerRel - ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER' - JOIN hs_office_relation debitorRel - ON debitorRel.anchorUuid = partnerRel.holderUuid AND debitorRel.type = 'DEBITOR' - WHERE debitorRel.uuid = debitor.debitorRelUuid) - || to_char(debitorNumberSuffix, 'fm00') as idName -FROM hs_office_debitor AS debitor - + call generateRbacIdentityViewFromQuery('hs_office_debitor', + $idName$ + SELECT debitor.uuid AS uuid, + 'D-' || (SELECT partner.partnerNumber + FROM hs_office_partner partner + JOIN hs_office_relation partnerRel + ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER' + JOIN hs_office_relation debitorRel + ON debitorRel.anchorUuid = partnerRel.holderUuid AND debitorRel.type = 'DEBITOR' + WHERE debitorRel.uuid = debitor.debitorRelUuid) + || to_char(debitorNumberSuffix, 'fm00') as idName + FROM hs_office_debitor AS debitor $idName$); --// diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index aa07dff7..89ccd7fa 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -35,10 +35,10 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); - SELECT r.* - FROM hs_office_partner AS p - JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid - WHERE p.uuid = NEW.partnerUuid + SELECT partnerRel.* + FROM hs_office_partner AS partner + JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid + WHERE partner.uuid = NEW.partnerUuid INTO newPartnerRel; assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid); @@ -138,20 +138,22 @@ execute procedure hs_office_membership_hs_office_relation_insert_tf(); An indirect role is a role FIXME. */ -create or replace function hs_office_membership_insert_permission_missing_tf() +create or replace function hs_office_membership_insert_permission_check_tf() returns trigger language plpgsql as $$ + +declare + superRoleObjectUuid uuid; + begin - if ( not hasInsertPermission( - ( SELECT partnerRel.uuid FROM + superRoleObjectUuid := (SELECT partnerRel.uuid + FROM hs_office_partner AS partner + JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid + WHERE partner.uuid = NEW.partnerUuid + ); + assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null'; - (SELECT r.* - FROM hs_office_partner AS p - JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid - WHERE p.uuid = NEW.partnerUuid - ) AS partnerRel - - ), 'INSERT', 'hs_office_membership') ) then + if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_membership') ) then raise exception '[403] insert into hs_office_membership not allowed for current subjects % (%)', currentSubjects(), currentSubjectsUuids(); @@ -162,19 +164,19 @@ end; $$; create trigger hs_office_membership_insert_permission_check_tg before insert on hs_office_membership for each row - execute procedure hs_office_membership_insert_permission_missing_tf(); + execute procedure hs_office_membership_insert_permission_check_tf(); --// -- ============================================================================ --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- - call generateRbacIdentityViewFromQuery('hs_office_membership', $idName$ - SELECT m.uuid AS uuid, - 'M-' || p.partnerNumber || m.memberNumberSuffix as idName -FROM hs_office_membership AS m -JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid - + call generateRbacIdentityViewFromQuery('hs_office_membership', + $idName$ + SELECT m.uuid AS uuid, + 'M-' || p.partnerNumber || m.memberNumberSuffix as idName + FROM hs_office_membership AS m + JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid $idName$); --//