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 aa0f925e..fec9c105 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 @@ -105,17 +105,19 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid { .createRole(OWNER, (with) -> { with.owningUser(CREATOR); with.incomingSuperRole(GLOBAL, ADMIN); - with.outgoingSubRole("bankAccount", REFERRER); with.permission(ALL); }) .createSubRole(ADMIN, (with) -> { with.permission(EDIT); }) .createSubRole(AGENT, (with) -> { + with.outgoingSubRole("bankAccount", REFERRER); with.outgoingSubRole("debitorRel", AGENT); }) .createSubRole(REFERRER, (with) -> { + with.incomingSuperRole("bankAccount", ADMIN); with.incomingSuperRole("debitorRel", AGENT); + with.outgoingSubRole("debitorRel", TENANT); with.permission(VIEW); }); } 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 42eb80da..dde18a66 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -13,6 +13,7 @@ import jakarta.validation.constraints.NotNull; import java.lang.reflect.InvocationTargetException; import java.util.*; +import static java.util.Optional.ofNullable; 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; @@ -300,6 +301,18 @@ public class RbacView { return this; } + boolean dependsOnColumn(final String columnName) { + return dependsRoleDefOnColumnName(this.superRoleDef, columnName) + || dependsRoleDefOnColumnName(this.subRoleDef, columnName); + } + + private Boolean dependsRoleDefOnColumnName(final RbacRoleDefinition superRoleDef, final String columnName) { + return ofNullable(superRoleDef) + .map(r -> r.getEntityAlias().dependsOnColum()) + .map(d -> columnName.equals(d.column)) + .orElse(false); + } + public enum GrantType { ROLE_TO_USER, ROLE_TO_ROLE, 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 64d14ed6..653702f7 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -4,10 +4,12 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import static java.util.stream.Collectors.*; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW; +import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.OLD; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.getRawTableName; @@ -79,12 +81,19 @@ class RolesGrantsAndPermissionsGenerator { }); }); - plPgSql.writeLn(""" - begin - if TG_OP <> 'INSERT' then - raise exception 'invalid usage of TRIGGER AFTER INSERT function'; - end if; - """); + plPgSql.indented(() -> { + plPgSql.writeLn("begin"); + generateCreateRolesAndGrantsAfterInsert(plPgSql); + generateUpdateRolesAndGrantsAfterUpdate(plPgSql); + plPgSql.ensureEmptyLine(); + plPgSql.writeLn("end; $$;"); + }); + plPgSql.writeLn(); + } + + private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) { + plPgSql.ensureEmptyLine(); + plPgSql.writeLn("if TG_OP = 'INSERT' then"); plPgSql.indented(() -> { @@ -106,8 +115,53 @@ class RolesGrantsAndPermissionsGenerator { generateGrants(plPgSql, PERM_TO_ROLE); }); - plPgSql.writeLn("end; $$;"); - plPgSql.writeLn(); + plPgSql.writeLn("end if;"); +} + + private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) { + plPgSql.ensureEmptyLine(); + plPgSql.writeLn("if TG_OP = 'UPDATE' then"); + + plPgSql.indented(() -> { + + rbacDef.getEntityAliases().values().stream() + .filter(ea -> !rbacDef.isRootEntityAlias(ea)) + .filter(ea -> ea.fetchSql() != null) + .forEach(ea -> { + plPgSql.writeLn( ea.fetchSql().sql.replace("${ref}", OLD.name()) + " into " + entityRefVar(OLD, ea) + ";"); + }); + + rbacDef.getEntityAliases().values().stream() + .map(RbacView.EntityAlias::dependsOnColum) + .filter(Objects::nonNull) + .filter(this::isUpdatable) + .map(c -> c.column) + .sorted() + .distinct() + .forEach(columnName -> { + plPgSql.writeLn(); + plPgSql.writeLn("if NEW." + columnName + " <> OLD." + columnName + " then"); + plPgSql.indented(() -> { + updateGrantsDependingOn(plPgSql, columnName); + }); + plPgSql.writeLn("end if;"); + }); + }); + + plPgSql.writeLn("end if;"); + } + + private boolean isUpdatable(final RbacView.Column c) { + return rbacDef.getUpdatableColumns().contains(c); + } + + private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) { + rbacDef.getGrantDefs().stream() + .filter(RbacView.RbacGrantDefinition::isToCreate) + .filter(g -> g.dependsOnColumn(columnName)) + .forEach(g -> { + plPgSql.writeLn("-- TODO: " + g); + }); } private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {