From 12010b4dae68f1fe96fbc0ddb72459943b9edf50 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 27 Feb 2024 12:34:52 +0100 Subject: [PATCH] rbacDef for HsOfficePartnerEntity and related amendments of generators --- .../office/debitor/HsOfficeDebitorEntity.java | 13 ++++---- .../office/partner/HsOfficePartnerEntity.java | 30 +++++++++++++++++ .../HsOfficeRelationshipEntity.java | 2 +- .../hsadminng/rbac/rbacdef/RbacView.java | 2 +- .../rbacdef/RbacViewMermaidFlowchart.java | 2 ++ .../rbacdef/RbacViewPostgresGenerator.java | 14 +++++--- .../RolesGrantsAndPermissionsGenerator.java | 33 ++++++++++++++----- 7 files changed, 75 insertions(+), 21 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 8905a34f..5c9c2fdb 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 @@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; +import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; @@ -107,7 +108,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { public static RbacView rbac() { return rbacViewFor("debitor", HsOfficeDebitorEntity.class) - .withIdentityView(RbacView.SQL.query(""" + .withIdentityView(SQL.query(""" SELECT debitor.uuid, 'D-' || (SELECT partner.partnerNumber FROM hs_office_partner partner @@ -117,7 +118,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' WHERE debitorRel.uuid = debitor.debitorRelUuid) || to_char(debitorNumberSuffix, 'fm00') - from hs_office_debitor as debitor; + from hs_office_debitor as debitor """)) .withUpdatableColumns( "debitorRel", @@ -131,11 +132,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { "defaultPrefix" /* TODO: do we want that updatable? */) .createPermission(custom("new-debitor")).grantedTo("global", ADMIN).pop() - .importProxyEntity("debitorRel", HsOfficeRelationshipEntity.class, + .importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class, fetchedBySql(""" SELECT * FROM hs_office_relationship AS r - WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid """), dependsOnColumn("debitorRelUuid")) .createPermission(ALL).grantedTo("debitorRel", OWNER).pop() @@ -146,7 +147,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { fetchedBySql(""" SELECT * FROM hs_office_relationship AS r - WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid """), dependsOnColumn("bankAccountUuid")) .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) @@ -156,7 +157,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { fetchedBySql(""" SELECT * FROM hs_office_relationship AS partnerRel - WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid; + WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid """), dependsOnColumn("debitorRelUuid")) .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) 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 342b601c..b4afb080 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 @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; +import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.NotFound; @@ -15,6 +16,12 @@ import jakarta.persistence.*; import java.util.Optional; import java.util.UUID; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.VIEW; +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.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -68,4 +75,27 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { public String toShortString() { return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse(""); } + + public static RbacView rbac() { + return rbacViewFor("partner", HsOfficePartnerEntity.class) + .withIdentityView(RbacView.SQL.query(""" + SELECT partner.partnerNumber + || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personuuid) + || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactuuid) + FROM hs_office_partner AD partner + $idName$) + """)) + .withUpdatableColumns( + "partnerRoleUuid", + "personUuid", + "contactUuid") + .createPermission(custom("new-partner")).grantedTo("global", ADMIN).pop() + + .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class, + fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"), + dependsOnColumn("partnerRelUuid")) + .createPermission(ALL).grantedTo("partnerRel", ADMIN).pop() + .createPermission(EDIT).grantedTo("partnerRel", AGENT).pop() + .createPermission(VIEW).grantedTo("partnerRel", TENANT).pop(); + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java index 80cd5607..eb433725 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java @@ -97,10 +97,10 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable { .createRole(OWNER, (with) -> { with.owningUser(CREATOR); with.incomingSuperRole(GLOBAL, ADMIN); - with.incomingSuperRole("anchorPerson", ADMIN); with.permission(ALL); }) .createSubRole(ADMIN, (with) -> { + with.incomingSuperRole("anchorPerson", ADMIN); with.permission(EDIT); }) .createSubRole(AGENT, (with) -> { 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 3f31ff08..b8ae11e4 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -101,7 +101,7 @@ public class RbacView { return this; } - public RbacView importProxyEntity( + public RbacView importRootEntityAliasProxy( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { if ( rootEntityAliasProxy != null ) { throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy); diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java index 634c4bf6..113bea26 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.rbac.rbacdef; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import org.apache.commons.lang3.StringUtils; @@ -162,6 +163,7 @@ public class RbacViewMermaidFlowchart { public static void main(String[] args) throws IOException { new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).generateToMarkdownFile(); new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).generateToMarkdownFile(); + new RbacViewMermaidFlowchart(HsOfficePartnerEntity.rbac()).generateToMarkdownFile(); new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).generateToMarkdownFile(); } } 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 bae8d498..f27e238f 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java @@ -1,5 +1,7 @@ package net.hostsharing.hsadminng.rbac.rbacdef; +import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import java.io.IOException; @@ -26,19 +28,15 @@ public class RbacViewPostgresGenerator { """ .replace("%{timestamp}", LocalDateTime.now().toString())); - // generateSqlForRelatedRbacObject(); - new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql); } - @Override public String toString() { return plPgSql.toString(); } - public static void main(String[] args) throws IOException { - final var rbac = HsOfficeRelationshipEntity.rbac(); + private static void generatePostgres(final RbacView rbac) throws IOException { final Path outputPath = Paths.get("doc", rbac.getRootEntityAlias().simpleName() + ".sql"); Files.writeString( outputPath, @@ -47,4 +45,10 @@ public class RbacViewPostgresGenerator { System.out.println(outputPath.toAbsolutePath()); } + + public static void main(String[] args) throws IOException { + generatePostgres(HsOfficeRelationshipEntity.rbac()); + generatePostgres(HsOfficePartnerEntity.rbac()); + generatePostgres(HsOfficeDebitorEntity.rbac()); + } } 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 7921cae6..cfe64710 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -84,12 +84,11 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.indented(() -> { - plPgSql.writeLn(); rbacDef.getEntityAliases().values().stream() .filter((ea) -> !rbacDef.isRootEntityAlias(ea)) .filter((ea) -> ea.fetchSql() != null) .forEach((ea) -> { - plPgSql.writeLn( ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";"); + plPgSql.writeLn( ea.fetchSql().sql.replace("${ref}", NEW.name()) + " into " + entityRefVar(NEW, ea) + ";"); }); createRolesWithGrantsSql(plPgSql, OWNER); @@ -99,12 +98,7 @@ class RolesGrantsAndPermissionsGenerator { createRolesWithGrantsSql(plPgSql, REFERRER); plPgSql.writeLn(); - rbacGrants - .forEach(g -> plPgSql.writeLn( - "call grantRoleToRole(${subRoleRef}, ${superRoleRef});" - .replace("${subRoleRef}", roleRef(NEW, g.getSubRoleDef()) ) - .replace("${superRoleRef}", roleRef(NEW, g.getSuperRoleDef()) )) - ); + rbacGrants.forEach(g -> plPgSql.writeLn(generateGrant(g))); plPgSql.writeLn("return NEW;"); }); @@ -113,11 +107,34 @@ class RolesGrantsAndPermissionsGenerator { plPgSql.writeLn(); } + private String generateGrant(RbacView.RbacGrantDefinition grantDef) { + return switch (grantDef.grantType()) { + case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant"); + case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}));" + .replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef()) ) + .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()) ); + case PERM_TO_ROLE -> "call grantPermissionsToRole(${permRef}, ${superRoleRef}));" + .replace("${permRef}", permRef(NEW, grantDef.getPermDef()) ) + .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()) ); + }; + } + + private String permRef(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { + return "createPermissions(${entityRef}.uuid, array ['${perm}']" + .replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias) + ? ref.name() + : "not implemented yet" ) // TODO + .replace("${perm}", permDef.permission.permission()); + } + private String getRawTableName(final Class entityClass) { return withoutRvSuffix(entityClass.getAnnotation(Table.class).name()); } private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) { + if ( roleDef == null ) { + System.out.println("null"); + } if ( roleDef.getEntityAlias().isGlobal()) { return "globalAdmin()"; }