From e6ef5b59c72e41469ff36bbd0b6d79a74b9c4029 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 25 Mar 2024 05:57:58 +0100 Subject: [PATCH] fix indrirect permission by indirect foreign key --- .../office/debitor/HsOfficeDebitorEntity.java | 8 +- .../membership/HsOfficeMembershipEntity.java | 4 +- .../relation/HsOfficeRelationEntity.java | 13 +- .../HsOfficeSepaMandateEntity.java | 9 +- .../rbac/rbacdef/InsertTriggerGenerator.java | 55 +++- .../hsadminng/rbac/rbacdef/RbacView.java | 286 ++++++++++++++++-- .../RbacViewMermaidFlowchartGenerator.java | 4 +- .../rbacdef/RbacViewPostgresGenerator.java | 4 +- .../rbac/rbacgrant/RawRbacGrantEntity.java | 6 +- .../rbac/rbacgrant/RbacGrantEntity.java | 4 +- .../hsadminng/test/dom/TestDomainEntity.java | 4 +- .../hsadminng/test/pac/TestPackageEntity.java | 4 +- .../resources/db/changelog/010-context.sql | 1 + .../resources/db/changelog/050-rbac-base.sql | 6 +- .../db/changelog/051-rbac-user-grant.sql | 56 ++-- .../db/changelog/054-rbac-context.sql | 1 - .../resources/db/changelog/055-rbac-views.sql | 14 +- .../db/changelog/113-test-customer-rbac.md | 4 +- .../db/changelog/113-test-customer-rbac.sql | 42 ++- .../db/changelog/123-test-package-rbac.md | 2 +- .../db/changelog/123-test-package-rbac.sql | 22 +- .../db/changelog/133-test-domain-rbac.md | 2 +- .../db/changelog/133-test-domain-rbac.sql | 37 ++- .../changelog/203-hs-office-contact-rbac.md | 2 +- .../changelog/203-hs-office-contact-rbac.sql | 20 +- .../db/changelog/213-hs-office-person-rbac.md | 2 +- .../changelog/213-hs-office-person-rbac.sql | 13 +- .../changelog/223-hs-office-relation-rbac.md | 2 +- .../changelog/223-hs-office-relation-rbac.sql | 36 +-- .../changelog/233-hs-office-partner-rbac.md | 2 +- .../changelog/233-hs-office-partner-rbac.sql | 39 +-- .../234-hs-office-partner-details-rbac.md | 2 +- .../234-hs-office-partner-details-rbac.sql | 7 +- .../243-hs-office-bankaccount-rbac.md | 2 +- .../243-hs-office-bankaccount-rbac.sql | 20 +- .../253-hs-office-sepamandate-rbac.md | 2 +- .../253-hs-office-sepamandate-rbac.sql | 13 +- .../changelog/273-hs-office-debitor-rbac.md | 2 +- .../changelog/273-hs-office-debitor-rbac.sql | 7 +- .../303-hs-office-membership-rbac.md | 2 +- .../303-hs-office-membership-rbac.sql | 13 +- src/test/resources/application.yml | 3 +- 42 files changed, 532 insertions(+), 245 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 948b3ae5..fcbf073e 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 @@ -21,6 +21,7 @@ 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.NOT_NULL; 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.*; @@ -165,8 +166,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { FROM hs_office_bankaccount AS b WHERE b.uuid = ${REF}.refundBankAccountUuid """), - NULLABLE - ) + NULLABLE) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) @@ -179,8 +179,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid WHERE partnerRel.type = 'PARTNER' AND ${REF}.debitorRelUuid = debitorRel.uuid - """) - ) + """), + NOT_NULL) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT) .toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT) 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 2c49ee03..dd7912cb 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 @@ -20,6 +20,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +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.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; @@ -135,7 +136,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { FROM hs_office_partner AS p JOIN hs_office_relation AS r ON r.uuid = p.partnerRelUuid WHERE p.uuid = ${REF}.partnerUuid - """)) + """), + NOT_NULL) .toRole("partnerRel", ADMIN).grantPermission(INSERT) .createRole(OWNER, (with) -> { 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 8278a774..f4b02875 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 @@ -16,6 +16,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; +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.*; @@ -90,16 +91,16 @@ 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") - ) + fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid"), + NOT_NULL) .importEntityAlias("holderPerson", HsOfficePersonEntity.class, dependsOnColumn("holderUuid"), - fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid") - ) + fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid"), + NOT_NULL) .importEntityAlias("contact", HsOfficeContactEntity.class, dependsOnColumn("contactUuid"), - fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid") - ) + fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid"), + NOT_NULL) .createRole(OWNER, (with) -> { with.owningUser(CREATOR); with.incomingSuperRole(GLOBAL, ADMIN); 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 21020f49..de5bc35c 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 @@ -21,6 +21,7 @@ import java.util.UUID; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; +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.*; @@ -110,12 +111,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid { FROM hs_office_relation debitorRel JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid WHERE debitor.uuid = ${REF}.debitorUuid - """) - ) + """), + NOT_NULL) .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"), - autoFetched() - ) + autoFetched(), + NOT_NULL) .createRole(OWNER, (with) -> { with.owningUser(CREATOR); 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 d625c0d7..e79f8acf 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java @@ -114,7 +114,16 @@ public class InsertTriggerGenerator { } } } else { - generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); + final var superRoleEntityAlias = g.getSuperRoleDef().getEntityAlias(); + + // TODO: Maybe this should depend on the indirection degree of the fetchSql? + // Maybe we need a separate fetchedBy method for all the simple, direct cases? + if (superRoleEntityAlias.fetchSql().sql.contains("JOIN ")) { + generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g); + } else { + generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g); + + } } }, () -> { @@ -149,6 +158,50 @@ public class InsertTriggerGenerator { with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName())); } + private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey( + final StringWriter plPgSql, + final RbacView.RbacGrantDefinition g) { + plPgSql.writeLn(""" + /** + Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}, + where the check is performed by an indirect role. + + An indirect role is a role FIXME. + */ + create or replace function ${rawSubTable}_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ + begin + if ( not hasInsertPermission( + ( SELECT ${varName}.uuid FROM + """, + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()), + with("varName", g.getSuperRoleDef().getEntityAlias().aliasName())); + plPgSql.indented(3, () -> { + plPgSql.writeLn( + "(" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ") AS ${varName}", + with("varName", g.getSuperRoleDef().getEntityAlias().aliasName()), + with("ref", NEW.name())); + }); + plPgSql.writeLn(""" + + ), 'INSERT', '${rawSubTable}') ) then + raise exception + '[403] insert into ${rawSubTable} not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); + end if; + return NEW; + end; $$; + + create trigger ${rawSubTable}_insert_permission_check_tg + before insert on ${rawSubTable} + for each row + execute procedure ${rawSubTable}_insert_permission_missing_tf(); + + """, + with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); + } + private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) { plPgSql.writeLn(""" /** 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 57499bb2..9b8f19cb 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -66,6 +66,21 @@ public class RbacView { private EntityAlias rootEntityAliasProxy; private RbacRoleDefinition previousRoleDef; + /** Crates an RBAC definition template for the given entity class and defining the given alias. + * + * @param alias + * an alias name for this entity/table, which can be used in further grants + * + * @param entityClass + * the Java class for which this RBAC definition is to be defined + * (the class to which the calling method belongs) + * + * @return + * the newly created RBAC definition template + * + * @param + * a JPA entity class extending RbacObject + */ public static RbacView rbacViewFor(final String alias, final Class entityClass) { return new RbacView(alias, entityClass); } @@ -77,22 +92,71 @@ public class RbacView { entityAliases.put("global", new EntityAlias("global")); } + /** + * Specifies, which columns of the restricted view are updatable at all. + * + * @param columnNames + * A list of the updatable columns. + * + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView withUpdatableColumns(final String... columnNames) { Collections.addAll(updatableColumns, columnNames); verifyVersionColumnExists(); return this; } + /** Specifies the SQL query which creates the identity view for this entity. + * + *

An identity view is a view which maps an objectUuid to an idName. + * The idName should be a human-readable representation of the row, but as short as possible. + * The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'. + * It's used to create the object-specific-role-names like test_customer#abc.admin - here 'abc' is the idName. + * The idName not necessarily unique in a table, but it should be avoided. + *

+ * + * @param sqlExpression + * Either specify an SQL projection (the part between SELECT and FROM), e.g. `SQL.projection("columnName") + * or the whole SELECT query returning the uuid and idName columns, + * e.g. `SQL.query("SELECT ... AS uuid, ... AS idName FROM ... JOIN ..."). + * Only add really important columns, just enough to create a short human-readable representation. + * + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView withIdentityView(final SQL sqlExpression) { this.identityViewSqlQuery = sqlExpression; return this; } + /** + * Specifies a ORDER BY clause for the generated restricted view. + * + *

A restricted view is generated, no matter if the order was specified or not.

+ * + * @param orderBySqlExpression + * That's the part behind `ORDER BY`, e.g. `SQL.expression("prefix"). + * + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) { this.orderBySqlExpression = orderBySqlExpression; return this; } + /** + * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table. + * + * @param role + * OWNER, ADMIN, AGENT etc. + * @param with + * a lambda which receives the created role to create grants and permissions to and from the newly created role, + * e.g. the owning user, incoming superroles, outgoing subroles + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView createRole(final Role role, final Consumer with) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); with.accept(newRoleDef); @@ -100,6 +164,15 @@ public class RbacView { return this; } + /** + * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table, + * which is becomes sub-role of the previously created role. + * + * @param role + * OWNER, ADMIN, AGENT etc. + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView createSubRole(final Role role) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); @@ -107,6 +180,19 @@ public class RbacView { return this; } + + /** + * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table, + * which is becomes sub-role of the previously created role. + * + * @param role + * OWNER, ADMIN, AGENT etc. + * @param with + * a lambda which receives the created role to create grants and permissions to and from the newly created role, + * e.g. the owning user, incoming superroles, outgoing subroles + * @return + * the `this` instance itself to allow chained calls. + */ public RbacView createSubRole(final Role role, final Consumer with) { final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate(); findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate(); @@ -115,10 +201,38 @@ public class RbacView { return this; } + /** + * Specifies that the given permission is to be created for each new row in the target table. + * + *

Grants to permissions created by this method have to be specified separately, + * often it's easier to read to use createRole/createSubRole and use with.permission(...).

+ * + * @param permission + * e.g. INSERT, SELECT, UPDATE, DELETE + * + * @return + * the newly created permission definition + */ public RbacPermissionDefinition createPermission(final Permission permission) { return createPermission(rootEntityAlias, permission); } + /** + * Specifies that the given permission is to be created for each new row in the target table, + * but for another table, e.g. a table with details data with different access rights. + * + *

Grants to permissions created by this method have to be specified separately, + * often it's easier to read to use createRole/createSubRole and use with.permission(...).

+ * + * @param entityAliasName + * A previously defined entity alias name. + * + * @param permission + * e.g. INSERT, SELECT, UPDATE, DELETE + * + * @return + * the newly created permission definition + */ public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) { return createPermission(findEntityAlias(entityAliasName), permission); } @@ -134,6 +248,32 @@ public class RbacView { return this; } + /** + * Imports the RBAC template from the given entity class and defines an alias name for it. + * This method is especially for proxy-entities, if the root entity does not have its own + * roles, a proxy-entity can be specified and its roles can be used instead. + * + * @param aliasName + * An alias name for the entity class. The same entity class can be imported multiple times, + * if multiple references to its table exist, then distinct alias names habe to be defined. + * + * @param entityClass + * A JPA entity class extending RbacObject which also implements an `rbac` method returning + * its RBAC specification. + * + * @param fetchSql + * An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the + * newly created or updated row (will be replaced by NEW/OLD from the trigger method). + * + * @param dependsOnColum + * The column, usually containing an uuid, on which this other table depends. + * + * @return + * the newly created permission definition + * + * @param + * a JPA entity class extending RbacObject + */ public RbacView importRootEntityAliasProxy( final String aliasName, final Class entityClass, @@ -146,6 +286,18 @@ public class RbacView { return this; } + /** + * Imports the RBAC template from the given entity class and defines an alias name for it. + * This method is especially to declare sub-entities, e.g. details to a main object. + * + * @see {@link} + * + * @return + * the newly created permission definition + * + * @param + * a JPA entity class extending RbacObject + */ public RbacView importSubEntityAlias( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { @@ -153,6 +305,33 @@ public class RbacView { return this; } + /** + * Imports the RBAC template from the given entity class and defines an anlias name for it. + * + * @param aliasName + * An alias name for the entity class. The same entity class can be imported multiple times, + * if multiple references to its table exist, then distinct alias names habe to be defined. + * + * @param entityClass + * A JPA entity class extending RbacObject which also implements an `rbac` method returning + * its RBAC specification. + * + * @param fetchSql + * An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the + * newly created or updated row (will be replaced by NEW/OLD from the trigger method). + * + * @param dependsOnColum + * The column, usually containing an uuid, on which this other table depends. + * + * @param nullable + * Specifies whether the dependsOnColum is nullable or not. + * + * @return + * the newly created permission definition + * + * @param + * a JPA entity class extending RbacObject + */ public RbacView importEntityAlias( final String aliasName, final Class entityClass, final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) { @@ -160,13 +339,7 @@ public class RbacView { return this; } - public RbacView importEntityAlias( - final String aliasName, final Class entityClass, - final Column dependsOnColum, final SQL fetchSql) { - importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL); - return this; - } - + // TODO: remove once it's not used in HsOffice...Entity anymore public RbacView importEntityAlias( final String aliasName, final Class entityClass, final Column dependsOnColum) { @@ -232,6 +405,16 @@ public class RbacView { } } + /** + * Starts declaring a grant to a given role. + * + * @param entityAlias + * A previously speciried entity alias name. + * @param role + * OWNER, ADMIN, AGENT, ... + * @return + * a grant builder + */ public RbacGrantBuilder toRole(final String entityAlias, final Role role) { return new RbacGrantBuilder(entityAlias, role); } @@ -430,6 +613,16 @@ public class RbacView { permDefs.add(this); } + /** + * Grants the permission under definition to the given role. + * + * @param entityAlias + * A previously declared entity alias name. + * @param role + * OWNER, ADMIN, ... + * @return + * The RbacView specification to which this permission definition belongs. + */ public RbacView grantedTo(final String entityAlias, final Role role) { findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate(); return RbacView.this; @@ -460,19 +653,61 @@ public class RbacView { return this; } + /** + * Specifies which user becomes the owner of newly created objects. + * @param userRole + * GLOBAL_ADMIN, CREATOR, ... + * @return + * The grant definition for further chained calls. + */ public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) { return grantRoleToUser(this, findUserRef(userRole)); } + /** + * Specifies which permission is to be created for newly created objects. + * @param permission + * INSERT, SELECT, ... + * @return + * The grant definition for further chained calls. + */ public RbacGrantDefinition permission(final Permission permission) { return grantPermissionToRole(createPermission(entityAlias, permission), this); } + /** + * Specifies in incoming super role which gets granted the role under definition. + * + *

Incoming means an incoming grant arrow in our grant-diagrams. + * Super-role means that it's the role to which another role is granted. + * Both means actually the same, just in different aspects.

+ * + * @param entityAlias + * A previously declared entity alias name. + * @param role + * OWNER, ADMIN, ... + * @return + * The grant definition for further chained calls. + */ public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) { final var incomingSuperRole = findRbacRole(entityAlias, role); return grantSubRoleToSuperRole(this, incomingSuperRole); } + /** + * Specifies in outgoing sub role which gets granted the role under definition. + * + *

Outgoing means an outgoing grant arrow in our grant-diagrams. + * Sub-role means which is granted to another role. + * Both means actually the same, just in different aspects.

+ * + * @param entityAlias + * A previously declared entity alias name. + * @param role + * OWNER, ADMIN, ... + * @return + * The grant definition for further chained calls. + */ public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) { final var outgoingSubRole = findRbacRole(entityAlias, role); return grantSubRoleToSuperRole(outgoingSubRole, this); @@ -802,6 +1037,26 @@ public class RbacView { } } + private static void generateRbacView(final Class c) { + final Method mainMethod = stream(c.getMethods()).filter( + m -> isStatic(m.getModifiers()) && m.getName().equals("main") + ) + .findFirst() + .orElse(null); + if (mainMethod != null) { + try { + mainMethod.invoke(null, new Object[] { null }); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated"); + } + } + + /** + * This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes. + */ public static void main(String[] args) { Stream.of( TestCustomerEntity.class, @@ -818,21 +1073,6 @@ public class RbacView { HsOfficeSepaMandateEntity.class, HsOfficeCoopSharesTransactionEntity.class, HsOfficeMembershipEntity.class - ).forEach(c -> { - final Method mainMethod = stream(c.getMethods()).filter( - m -> isStatic(m.getModifiers()) && m.getName().equals("main") - ) - .findFirst() - .orElse(null); - if (mainMethod != null) { - try { - mainMethod.invoke(null, new Object[] { null }); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } else { - System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated"); - } - }); + ).forEach(RbacView::generateRbacView); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java index ccef566d..d6a9bc28 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java @@ -4,7 +4,6 @@ import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import java.nio.file.*; -import java.time.LocalDateTime; import static java.util.stream.Collectors.joining; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*; @@ -149,14 +148,13 @@ public class RbacViewMermaidFlowchartGenerator { """ ### rbac %{entityAlias} - This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}. + This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %{flowchart} ``` """ .replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName()) - .replace("%{timestamp}", LocalDateTime.now().toString()) .replace("%{flowchart}", flowchart.toString()), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); System.out.println("Markdown-File: " + path.toAbsolutePath()); 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 9850d942..5a3b2be8 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java @@ -5,7 +5,6 @@ import lombok.SneakyThrows; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with; @@ -21,10 +20,9 @@ public class RbacViewPostgresGenerator { liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-"); plPgSql.writeLn(""" --liquibase formatted sql - -- This code generated was by ${generator} at ${timestamp}. + -- This code generated was by ${generator}, do not amend manually. """, with("generator", getClass().getSimpleName()), - with("timestamp", LocalDateTime.now().toString()), with("ref", NEW.name())); new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java index 77a2d027..f7b3cdf4 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java @@ -28,8 +28,8 @@ public class RawRbacGrantEntity implements Comparable { @Column(name = "grantedbyroleidname", updatable = false, insertable = false) private String grantedByRoleIdName; - @Column(name = "usergrantsbyroleuuid", updatable = false, insertable = false) - private UUID userGrantsByRoleUuid; + @Column(name = "grantedbyroleuuid", updatable = false, insertable = false) + private UUID grantedByRoleUuid; @Column(name = "ascendantidname", updatable = false, insertable = false) private String ascendantIdName; @@ -50,7 +50,7 @@ public class RawRbacGrantEntity implements Comparable { // @formatter:off return "{ grant " + descendantIdName + " to " + ascendantIdName + - " by " + ( userGrantsByRoleUuid == null + " by " + ( grantedByRoleUuid == null ? "system" : grantedByRoleIdName ) + ( assumed ? " and assume" : "") + diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java index 6f175b01..a3abf528 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java @@ -22,8 +22,8 @@ public class RbacGrantEntity { @Column(name = "grantedbyroleidname", updatable = false, insertable = false) private String grantedByRoleIdName; - @Column(name = "usergrantsbyroleuuid", updatable = false, insertable = false) - private UUID userGrantsByRoleUuid; + @Column(name = "grantedbyroleuuid", updatable = false, insertable = false) + private UUID grantedByRoleUuid; @Column(name = "grantedroleidname", updatable = false, insertable = false) private String grantedRoleIdName; diff --git a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java index fe053f1f..4fb82a18 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +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.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; @@ -52,7 +53,8 @@ public class TestDomainEntity implements HasUuid { fetchedBySql(""" SELECT * FROM test_package p WHERE p.uuid= ${ref}.packageUuid - """)) + """), + NOT_NULL) .toRole("package", ADMIN).grantPermission(INSERT) .createRole(OWNER, (with) -> { diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java index 9dc0d5d9..27546cf2 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +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.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*; @@ -53,7 +54,8 @@ public class TestPackageEntity implements HasUuid { fetchedBySql(""" SELECT * FROM test_customer c WHERE c.uuid= ${ref}.customerUuid - """)) + """), + NOT_NULL) .toRole("customer", ADMIN).grantPermission(INSERT) .createRole(OWNER, (with) -> { diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql index 8de41891..0e5cc457 100644 --- a/src/main/resources/db/changelog/010-context.sql +++ b/src/main/resources/db/changelog/010-context.sql @@ -160,6 +160,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar) declare cleanIdentifier varchar; begin + -- TODO: remove the ':' from the list of allowed characters as soon as it's not used anymore cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g'); return cleanIdentifier; end; $$; diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index 9ae9f2ff..735f1932 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -300,7 +300,7 @@ create or replace function deleteRbacGrantsOfRbacRole() strict as $$ begin if TG_OP = 'DELETE' then - delete from RbacGrants g where old.uuid in (g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid); + delete from RbacGrants g where old.uuid in (g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid); else raise exception 'invalid usage of TRIGGER BEFORE DELETE'; end if; @@ -519,12 +519,12 @@ create table RbacGrants ( uuid uuid primary key default uuid_generate_v4(), grantedByTriggerOf uuid references RbacObject (uuid) on delete cascade initially deferred , - userGrantsByRoleUuid uuid references RbacRole (uuid), + grantedByRoleUuid uuid references RbacRole (uuid), ascendantUuid uuid references RbacReference (uuid), descendantUuid uuid references RbacReference (uuid), assumed boolean not null default true, -- auto assumed (true) vs. needs assumeRoles (false) unique (ascendantUuid, descendantUuid), - constraint rbacGrant_createdBy check ( userGrantsByRoleUuid is null or grantedByTriggerOf is null) ); + constraint rbacGrant_createdBy check ( grantedByRoleUuid is null or grantedByTriggerOf is null) ); create index on RbacGrants (ascendantUuid); create index on RbacGrants (descendantUuid); diff --git a/src/main/resources/db/changelog/051-rbac-user-grant.sql b/src/main/resources/db/changelog/051-rbac-user-grant.sql index 090d3cf4..a82865c8 100644 --- a/src/main/resources/db/changelog/051-rbac-user-grant.sql +++ b/src/main/resources/db/changelog/051-rbac-user-grant.sql @@ -20,52 +20,50 @@ begin return currentSubjectsUuids[1]; end; $$; -create or replace procedure grantRoleToUserUnchecked(userGrantsByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true) +create or replace procedure grantRoleToUserUnchecked(grantedByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true) language plpgsql as $$ begin - perform assertReferenceType('grantingRoleUuid', userGrantsByRoleUuid, 'RbacRole'); + perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole'); perform assertReferenceType('roleId (descendant)', roleUuid, 'RbacRole'); perform assertReferenceType('userId (ascendant)', userUuid, 'RbacUser'); - raise notice 'role % grants role % to user %, assumed=%', userGrantsByRoleUuid, roleUuid, userUuid, doAssume; - insert - into RbacGrants (userGrantsByRoleUuid, ascendantUuid, descendantUuid, assumed) - values (userGrantsByRoleUuid, userUuid, roleUuid, doAssume); + into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed) + values (grantedByRoleUuid, userUuid, roleUuid, doAssume); -- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same? -- Most powerful or latest grant wins? What about managed? -- on conflict do nothing; -- allow granting multiple times end; $$; -create or replace procedure grantRoleToUser(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true) +create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true) language plpgsql as $$ declare grantedByRoleIdName text; grantedRoleIdName text; begin - perform assertReferenceType('grantingRoleUuid', userGrantsByRoleUuid, 'RbacRole'); + perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole'); perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole'); perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser'); - assert userGrantsByRoleUuid is not null, 'userGrantsByRoleUuid must not be null'; + assert grantedByRoleUuid is not null, 'grantedByRoleUuid must not be null'; assert grantedRoleUuid is not null, 'grantedRoleUuid must not be null'; assert userUuid is not null, 'userUuid must not be null'; - if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then - select roleIdName from rbacRole_ev where uuid=userGrantsByRoleUuid into grantedByRoleIdName; + if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then + select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName; raise exception '[403] Access to granted-by-role % (%) forbidden for % (%)', - grantedByRoleIdName, userGrantsByRoleUuid, currentSubjects(), currentSubjectsUuids(); + grantedByRoleIdName, grantedByRoleUuid, currentSubjects(), currentSubjectsUuids(); end if; - if NOT isGranted(userGrantsByRoleUuid, grantedRoleUuid) then - select roleIdName from rbacRole_ev where uuid=userGrantsByRoleUuid into grantedByRoleIdName; + if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then + select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName; select roleIdName from rbacRole_ev where uuid=grantedRoleUuid into grantedRoleIdName; raise exception '[403] Access to granted role % (%) forbidden for % (%)', - grantedRoleIdName, grantedRoleUuid, grantedByRoleIdName, userGrantsByRoleUuid; + grantedRoleIdName, grantedRoleUuid, grantedByRoleIdName, grantedByRoleUuid; end if; insert - into RbacGrants (userGrantsByRoleUuid, ascendantUuid, descendantUuid, assumed) - values (userGrantsByRoleUuid, userUuid, grantedRoleUuid, doAssume); + into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed) + values (grantedByRoleUuid, userUuid, grantedRoleUuid, doAssume); -- TODO.spec: What should happen on mupltiple grants? What if options (doAssume) are not the same? -- Most powerful or latest grant wins? What about managed? -- on conflict do nothing; -- allow granting multiple times @@ -77,40 +75,40 @@ end; $$; --changeset rbac-user-grant-REVOKE-ROLE-FROM-USER:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -create or replace procedure checkRevokeRoleFromUserPreconditions(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) +create or replace procedure checkRevokeRoleFromUserPreconditions(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) language plpgsql as $$ begin - perform assertReferenceType('userGrantsByRoleUuid', userGrantsByRoleUuid, 'RbacRole'); + perform assertReferenceType('grantedByRoleUuid', grantedByRoleUuid, 'RbacRole'); perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole'); perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser'); - if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then - raise exception '[403] Revoking role created by % is forbidden for %.', userGrantsByRoleUuid, currentSubjects(); + if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then + raise exception '[403] Revoking role created by % is forbidden for %.', grantedByRoleUuid, currentSubjects(); end if; - if NOT isGranted(userGrantsByRoleUuid, grantedRoleUuid) then + if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then raise exception '[403] Revoking role % is forbidden for %.', grantedRoleUuid, currentSubjects(); end if; - --raise exception 'isGranted(%, %)', currentSubjectsUuids(), userGrantsByRoleUuid; - if NOT isGranted(currentSubjectsUuids(), userGrantsByRoleUuid) then - raise exception '[403] Revoking role granted by % is forbidden for %.', userGrantsByRoleUuid, currentSubjects(); + --raise exception 'isGranted(%, %)', currentSubjectsUuids(), grantedByRoleUuid; + if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then + raise exception '[403] Revoking role granted by % is forbidden for %.', grantedByRoleUuid, currentSubjects(); end if; if NOT isGranted(userUuid, grantedRoleUuid) then - raise exception '[404] No such grant found granted by % for user % to role %.', userGrantsByRoleUuid, userUuid, grantedRoleUuid; + raise exception '[404] No such grant found granted by % for user % to role %.', grantedByRoleUuid, userUuid, grantedRoleUuid; end if; end; $$; -create or replace procedure revokeRoleFromUser(userGrantsByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) +create or replace procedure revokeRoleFromUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid) language plpgsql as $$ begin - call checkRevokeRoleFromUserPreconditions(userGrantsByRoleUuid, grantedRoleUuid, userUuid); + call checkRevokeRoleFromUserPreconditions(grantedByRoleUuid, grantedRoleUuid, userUuid); raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', userUuid, grantedRoleUuid; delete from RbacGrants as g where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid - and g.userGrantsByRoleUuid = revokeRoleFromUser.userGrantsByRoleUuid; + and g.grantedByRoleUuid = revokeRoleFromUser.grantedByRoleUuid; end; $$; --// diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/054-rbac-context.sql index b5b554e5..5437131f 100644 --- a/src/main/resources/db/changelog/054-rbac-context.sql +++ b/src/main/resources/db/changelog/054-rbac-context.sql @@ -139,7 +139,6 @@ begin raise exception '[401] currentUserUuid cannot be determined, please call `defineContext(...)` first;"'; end if; end if; - raise notice 'currentUserUuid %', currentUserUuid; return currentUserUuid::uuid; end; $$; --// diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql index e13f66ce..b494d120 100644 --- a/src/main/resources/db/changelog/055-rbac-views.sql +++ b/src/main/resources/db/changelog/055-rbac-views.sql @@ -60,14 +60,14 @@ create or replace view rbacgrants_ev as go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || '.' || r.roletype as grantedByRoleIdName, x.ascendingIdName as ascendantIdName, x.descendingIdName as descendantIdName, - x.userGrantsByRoleUuid, + x.grantedByRoleUuid, x.ascendantUuid as ascendantUuid, x.descendantUuid as descendantUuid, x.assumed from ( select g.uuid as grantUuid, g.grantedbytriggerof as grantedbytriggerof, - g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid, g.assumed, + g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed, coalesce( 'user ' || au.name, @@ -91,7 +91,7 @@ create or replace view rbacgrants_ev as left outer join rbacpermission dp on dp.uuid = g.descendantUuid left outer join rbacobject as dpo on dpo.uuid = dp.objectUuid ) as x - left outer join rbacrole as r on r.uuid = userGrantsByRoleUuid + left outer join rbacrole as r on r.uuid = grantedByRoleUuid left outer join rbacuser u on u.uuid = x.ascendantuuid left outer join rbacobject go on go.uuid = r.objectuuid @@ -112,10 +112,10 @@ create or replace view rbacgrants_rv as -- @formatter:off select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || '.' || r.roletype as grantedByRoleIdName, g.objectTable || '#' || g.objectIdName || '.' || g.roletype as grantedRoleIdName, g.userName, g.assumed, - g.userGrantsByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid, + g.grantedByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid, g.objectTable, g.objectUuid, g.objectIdName, g.roleType as grantedRoleType from ( - select g.userGrantsByRoleUuid, g.ascendantuuid, g.descendantuuid, g.assumed, + select g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed, u.name as userName, o.objecttable, r.objectuuid, r.roletype, findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName from rbacgrants as g @@ -124,7 +124,7 @@ select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || left outer join rbacuser u on u.uuid = g.ascendantuuid where isGranted(currentSubjectsUuids(), r.uuid) ) as g - join RbacRole as r on r.uuid = userGrantsByRoleUuid + join RbacRole as r on r.uuid = grantedByRoleUuid join RbacObject as o on o.uuid = r.objectUuid order by grantedRoleIdName; -- @formatter:on @@ -177,7 +177,7 @@ create or replace function deleteRbacGrant() returns trigger language plpgsql as $$ begin - call revokeRoleFromUser(old.userGrantsByRoleUuid, old.grantedRoleUuid, old.userUuid); + call revokeRoleFromUser(old.grantedByRoleUuid, old.grantedRoleUuid, old.userUuid); return old; end; $$; diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.md b/src/main/resources/db/changelog/113-test-customer-rbac.md index 438b6254..4d63eeac 100644 --- a/src/main/resources/db/changelog/113-test-customer-rbac.md +++ b/src/main/resources/db/changelog/113-test-customer-rbac.md @@ -1,6 +1,6 @@ ### rbac customer -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.425403022. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% @@ -21,6 +21,7 @@ subgraph customer["`**customer**`"] subgraph customer:permissions[ ] style customer:permissions fill:#dd4901,stroke:white + perm:customer:INSERT{{customer:INSERT}} perm:customer:DELETE{{customer:DELETE}} perm:customer:UPDATE{{customer:UPDATE}} perm:customer:SELECT{{customer:SELECT}} @@ -36,6 +37,7 @@ role:customer:owner ==> role:customer:admin role:customer:admin ==> role:customer:tenant %% granting permissions to roles +role:global:admin ==> perm:customer:INSERT role:customer:owner ==> perm:customer:DELETE role:customer:admin ==> perm:customer:UPDATE role:customer:tenant ==> perm:customer:SELECT 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 dccb3a26..b98fc949 100644 --- a/src/main/resources/db/changelog/113-test-customer-rbac.sql +++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-22T14:44:19.441879428. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -80,6 +80,46 @@ execute procedure insertTriggerForTestCustomer_tf(); --changeset test-customer-rbac-INSERT:1 endDelimiter:--// -- ---------------------------------------------------------------------------- +/* + Creates INSERT INTO test_customer permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO test_customer permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalAdmin()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds test_customer INSERT permission to specified role of new global rows. +*/ +create or replace function test_customer_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + createPermission(NEW.uuid, 'INSERT', 'test_customer'), + globalAdmin()); + return NEW; +end; $$; + +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist +create trigger z_test_customer_global_insert_tg + after insert on global + for each row +execute procedure test_customer_global_insert_tf(); + /** Checks if the user or assumed roles are allowed to insert a row to test_customer, where only global-admin has that permission. diff --git a/src/main/resources/db/changelog/123-test-package-rbac.md b/src/main/resources/db/changelog/123-test-package-rbac.md index 895d3269..34b8c7c7 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.md +++ b/src/main/resources/db/changelog/123-test-package-rbac.md @@ -1,6 +1,6 @@ ### rbac package -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.484173294. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 21c4e005..912430bb 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-22T12:01:44.554331877. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -195,32 +195,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 c - WHERE c.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(); --// diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.md b/src/main/resources/db/changelog/133-test-domain-rbac.md index 4f507312..6954e9b8 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.md +++ b/src/main/resources/db/changelog/133-test-domain-rbac.md @@ -1,6 +1,6 @@ ### rbac domain -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-22T14:44:19.510830235. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 796fba35..6344e43d 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.sql +++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql @@ -1,5 +1,6 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + -- ============================================================================ --changeset test-domain-rbac-OBJECT:1 endDelimiter:--// @@ -33,9 +34,12 @@ declare begin call enterTriggerForObjectUuid(NEW.uuid); + SELECT * FROM test_package p WHERE p.uuid= NEW.packageUuid - into newPackage; + INTO newPackage; + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); + perform createRoleWithGrants( testDomainOwner(NEW), @@ -71,9 +75,9 @@ create trigger insertTriggerForTestDomain_tg after insert on test_domain for each row execute procedure insertTriggerForTestDomain_tf(); - --// + -- ============================================================================ --changeset test-domain-rbac-update-trigger:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -97,14 +101,18 @@ begin SELECT * FROM test_package p WHERE p.uuid= OLD.packageUuid - into oldPackage; + INTO oldPackage; + assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid); + SELECT * FROM test_package p WHERE p.uuid= NEW.packageUuid - into newPackage; + INTO newPackage; + assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid); + if NEW.packageUuid <> OLD.packageUuid then - call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage)); + call revokePermissionFromRole(getPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage)); call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage)); call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage)); @@ -137,9 +145,9 @@ create trigger updateTriggerForTestDomain_tg after update on test_domain for each row execute procedure updateTriggerForTestDomain_tf(); - --// + -- ============================================================================ --changeset test-domain-rbac-INSERT:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -178,13 +186,17 @@ begin return NEW; end; $$; +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist create trigger z_test_domain_test_package_insert_tg after insert on test_package for each row execute procedure test_domain_test_package_insert_tf(); /** - Checks if the user or assumed roles are allowed to insert a row to test_domain. + Checks if the user or assumed roles are allowed to insert a row to test_domain, + where the check is performed by a direct role. + + 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 @@ -199,8 +211,8 @@ create trigger test_domain_insert_permission_check_tg for each row when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') ) execute procedure test_domain_insert_permission_missing_tf(); - --// + -- ============================================================================ --changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -208,13 +220,15 @@ create trigger test_domain_insert_permission_check_tg call generateRbacIdentityViewFromProjection('test_domain', $idName$ name $idName$); - --// + -- ============================================================================ --changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacRestrictedView('test_domain', - 'name', + $orderBy$ + name + $orderBy$, $updates$ version = new.version, packageUuid = new.packageUuid, @@ -222,4 +236,3 @@ call generateRbacRestrictedView('test_domain', $updates$); --// - diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.md b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md index 20331ece..52584907 100644 --- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.md +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md @@ -1,6 +1,6 @@ ### rbac contact -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-14T09:00:15.762621659. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 ee40d154..51c244a8 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-14T09:00:15.769718298. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -109,26 +109,16 @@ create or replace function hs_office_contact_global_insert_tf() strict as $$ begin call grantPermissionToRole( - globalGuest(), - createPermission(NEW.uuid, 'INSERT', 'hs_office_contact')); + createPermission(NEW.uuid, 'INSERT', 'hs_office_contact'), + globalGuest()); return NEW; end; $$; +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist create trigger z_hs_office_contact_global_insert_tg after insert on global for each row execute procedure hs_office_contact_global_insert_tf(); - -/** - Checks if the user or assumed roles are allowed to insert a row to hs_office_contact. -*/ -create or replace function hs_office_contact_insert_permission_missing_tf() - returns trigger - language plpgsql as $$ -begin - raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)', - currentSubjects(), currentSubjectsUuids(); -end; $$; --// -- ============================================================================ @@ -148,7 +138,7 @@ call generateRbacRestrictedView('hs_office_contact', label $orderBy$, $updates$ - label = new.label, + label = new.label, postalAddress = new.postalAddress, emailAddresses = new.emailAddresses, phoneNumbers = new.phoneNumbers diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.md b/src/main/resources/db/changelog/213-hs-office-person-rbac.md index dc353216..70e0f33a 100644 --- a/src/main/resources/db/changelog/213-hs-office-person-rbac.md +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.md @@ -1,6 +1,6 @@ ### rbac person -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T13:35:44.716916229. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 104f3009..d1cbf39b 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-18T13:35:44.726508114. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -142,15 +142,6 @@ call generateRbacRestrictedView('hs_office_person', tradeName = new.tradeName, givenName = new.givenName, familyName = new.familyName - $updates$ - , - $columns$ - uuid, - personType, - tradeName, - givenName, - familyName - $columns$ - ); + $updates$); --// diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md index 7a72bfd2..8e5524ec 100644 --- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md +++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md @@ -1,6 +1,6 @@ ### rbac relation -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-15T17:17:00.854621634. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 2b52f837..df556a46 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-15T17:17:00.864301165. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -65,21 +65,21 @@ begin perform createRoleWithGrants( hsOfficeRelationAgent(NEW), incomingSuperRoles => array[ - hsOfficePersonAdmin(newHolderPerson), - hsOfficeRelationAdmin(NEW)] + hsOfficeRelationAdmin(NEW), + hsOfficePersonAdmin(newHolderPerson)] ); perform createRoleWithGrants( hsOfficeRelationTenant(NEW), permissions => array['SELECT'], incomingSuperRoles => array[ - hsOfficeRelationAgent(NEW), + hsOfficePersonAdmin(newHolderPerson), hsOfficeContactAdmin(newContact), - hsOfficePersonAdmin(newHolderPerson)], + hsOfficeRelationAgent(NEW)], outgoingSubRoles => array[ - hsOfficeContactReferrer(newContact), hsOfficePersonReferrer(newHolderPerson), - hsOfficePersonReferrer(newAnchorPerson)] + hsOfficePersonReferrer(newAnchorPerson), + hsOfficeContactReferrer(newContact)] ); call leaveTriggerForObjectUuid(NEW.uuid); @@ -220,34 +220,30 @@ begin return NEW; end; $$; +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist create trigger z_hs_office_relation_hs_office_person_insert_tg after insert on hs_office_person for each row 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. + Checks if the user or assumed roles are allowed to insert a row to hs_office_relation, + where the check is performed by a direct role. + + 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(); --// @@ -271,7 +267,7 @@ call generateRbacRestrictedView('hs_office_relation', (select idName from hs_office_person_iv p where p.uuid = target.holderUuid) $orderBy$, $updates$ - contactUuid = new.contactUuid + contactUuid = new.contactUuid $updates$); --// diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md index dd87eaed..98bd276d 100644 --- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md @@ -1,6 +1,6 @@ ### rbac partner -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T09:31:47.361311186. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 725ab536..33a5c07b 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-18T09:31:47.368892199. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -131,36 +131,6 @@ begin end if; call leaveTriggerForObjectUuid(NEW.uuid); - - -- raise exception 'RBAC updated from rel % to %', OLD.partnerReluuid, NEW.partnerReluuid; -end; $$; - - - -create or replace procedure updateRbacRulesForHsOfficePartnerX( - OLD hs_office_partner, - NEW hs_office_partner -) - language plpgsql as $$ -declare - partnerRel hs_office_relation; - grantCount int; - -begin - assert OLD.uuid = NEW.uuid, 'uuid did change, but should not'; - assert OLD.partnerReluuid <> NEW.partnerReluuid, 'partnerReluuid did not change, but should have'; - - delete from rbacgrants where grantedbytriggerof = OLD.uuid; - select count(*) from rbacgrants where grantedbytriggerof=NEW.uuid into grantCount; - assert grantCount=0, format('unexpected grantCount>0: %d', grantCount); - - call buildRbacSystemForHsOfficePartner(NEW); - select * from hs_office_relation where uuid=NEW.partnerReluuid into partnerRel; - call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(partnerRel)); - select count(*) from rbacgrants where grantedbytriggerof=NEW.uuid into grantCount; - assert grantCount>0, format('unexpected grantCount=0: %d', grantCount); - raise warning 'WARNING grantCount=%', grantCount; - end; $$; /* @@ -172,7 +142,7 @@ create or replace function updateTriggerForHsOfficePartner_tf() language plpgsql strict as $$ begin - call updateRbacRulesForHsOfficePartnerX(OLD, NEW); + call updateRbacRulesForHsOfficePartner(OLD, NEW); return NEW; end; $$; @@ -228,7 +198,8 @@ create trigger z_hs_office_partner_global_insert_tg execute procedure hs_office_partner_global_insert_tf(); /** - Checks if the user or assumed roles are allowed to insert a row to hs_office_partner. + Checks if the user or assumed roles are allowed to insert a row to hs_office_partner, + where only global-admin has that permission. */ create or replace function hs_office_partner_insert_permission_missing_tf() returns trigger @@ -262,7 +233,7 @@ call generateRbacRestrictedView('hs_office_partner', 'P-' || partnerNumber $orderBy$, $updates$ - partnerRelUuid = new.partnerRelUuid + partnerRelUuid = new.partnerRelUuid $updates$); --// diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md index 9dbe4328..d27a1064 100644 --- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md +++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md @@ -1,6 +1,6 @@ ### rbac partnerDetails -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-16T12:04:37.309540020. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 b6700048..40ba9b80 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-16T12:04:37.319601283. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -102,7 +102,8 @@ create trigger z_hs_office_partner_details_global_insert_tg execute procedure hs_office_partner_details_global_insert_tf(); /** - Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details. + Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details, + where only global-admin has that permission. */ create or replace function hs_office_partner_details_insert_permission_missing_tf() returns trigger @@ -140,7 +141,7 @@ call generateRbacRestrictedView('hs_office_partner_details', uuid $orderBy$, $updates$ - registrationOffice = new.registrationOffice, + registrationOffice = new.registrationOffice, registrationNumber = new.registrationNumber, birthPlace = new.birthPlace, birthName = new.birthName, diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md index c41ea375..c33e3374 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md @@ -1,6 +1,6 @@ ### rbac bankAccount -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-14T08:55:11.118624882. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 7b74f380..c8ba461d 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-14T08:55:11.127959896. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -109,26 +109,16 @@ 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; $$; +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist create trigger z_hs_office_bankaccount_global_insert_tg after insert on global for each row execute procedure hs_office_bankaccount_global_insert_tf(); - -/** - Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount. -*/ -create or replace function hs_office_bankaccount_insert_permission_missing_tf() - returns trigger - language plpgsql as $$ -begin - raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)', - currentSubjects(), currentSubjectsUuids(); -end; $$; --// -- ============================================================================ @@ -148,7 +138,7 @@ call generateRbacRestrictedView('hs_office_bankaccount', iban $orderBy$, $updates$ - holder = new.holder, + holder = new.holder, iban = new.iban, bic = new.bic $updates$); diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md index 528cf6eb..43fb6ef3 100644 --- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md @@ -1,6 +1,6 @@ ### rbac sepaMandate -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-18T16:07:14.011240343. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 98ce69b8..7d997ec1 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-18T16:07:14.019894954. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -64,8 +64,8 @@ begin hsOfficeSepaMandateAgent(NEW), incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)], outgoingSubRoles => array[ - hsOfficeRelationAgent(newDebitorRel), - hsOfficeBankAccountReferrer(newBankAccount)] + hsOfficeBankAccountReferrer(newBankAccount), + hsOfficeRelationAgent(newDebitorRel)] ); perform createRoleWithGrants( @@ -146,7 +146,10 @@ create trigger z_hs_office_sepamandate_hs_office_relation_insert_tg execute procedure hs_office_sepamandate_hs_office_relation_insert_tf(); /** - Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate. + Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate, + where the check is performed by an indirect role. + + An indirect role is a role FIXME. */ create or replace function hs_office_sepamandate_insert_permission_missing_tf() returns trigger @@ -195,7 +198,7 @@ call generateRbacRestrictedView('hs_office_sepamandate', validity $orderBy$, $updates$ - reference = new.reference, + reference = new.reference, agreement = new.agreement, validity = new.validity $updates$); 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 157765ac..a1baa702 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,6 +1,6 @@ ### rbac debitor -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-16T13:52:18.484919583. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 d2f12e02..1999547d 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,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-20T13:55:16.722860098. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -177,7 +177,8 @@ create trigger z_hs_office_debitor_global_insert_tg 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. + Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor, + where only global-admin has that permission. */ create or replace function hs_office_debitor_insert_permission_missing_tf() returns trigger @@ -221,7 +222,7 @@ call generateRbacRestrictedView('hs_office_debitor', defaultPrefix $orderBy$, $updates$ - debitorRelUuid = new.debitorRelUuid, + debitorRelUuid = new.debitorRelUuid, billable = new.billable, refundBankAccountUuid = new.refundBankAccountUuid, vatId = new.vatId, diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md index c6ccf003..f4b856ca 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md @@ -1,6 +1,6 @@ ### rbac membership -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-21T17:09:08.826781619. +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% 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 5531e9f8..aa07dff7 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 @@ -1,5 +1,5 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-21T17:09:08.832004329. +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. -- ============================================================================ @@ -54,8 +54,8 @@ begin hsOfficeMembershipAdmin(NEW), permissions => array['UPDATE'], incomingSuperRoles => array[ - hsOfficeMembershipOwner(NEW), - hsOfficeRelationAgent(newPartnerRel)] + hsOfficeRelationAgent(newPartnerRel), + hsOfficeMembershipOwner(NEW)] ); perform createRoleWithGrants( @@ -133,7 +133,10 @@ create trigger z_hs_office_membership_hs_office_relation_insert_tg execute procedure hs_office_membership_hs_office_relation_insert_tf(); /** - Checks if the user or assumed roles are allowed to insert a row to hs_office_membership. + Checks if the user or assumed roles are allowed to insert a row to hs_office_membership, + where the check is performed by an indirect role. + + An indirect role is a role FIXME. */ create or replace function hs_office_membership_insert_permission_missing_tf() returns trigger @@ -183,7 +186,7 @@ call generateRbacRestrictedView('hs_office_membership', validity $orderBy$, $updates$ - validity = new.validity, + validity = new.validity, membershipFeeBillable = new.membershipFeeBillable, reasonForTermination = new.reasonForTermination $updates$); diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index a4f570f9..40ae85bb 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,8 +4,9 @@ spring: platform: postgres datasource: - url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers + url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers url-local: jdbc:postgresql://localhost:5432/postgres + url: ${spring.datasource.url-tc} username: postgres password: password