From 3e2fa5a6f6f64f324edf5e4c57eada8a3fcffd42 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 23 Feb 2024 16:09:10 +0100 Subject: [PATCH] rekursive Entity-Imports and render complex Mermad-Flowcharts (example: Debitor with parterRel+personRel and holderPerspn+anchorPerson each) --- .../HsOfficeBankAccountEntity.java | 71 +------------ .../office/debitor/HsOfficeDebitorEntity.java | 74 +++++++++++++ .../office/person/HsOfficePersonEntity.java | 23 ++++ .../HsOfficeRelationshipEntity.java | 38 +++++++ .../hsadminng/rbac/rbacdef/RbacView.java | 100 ++++++++++++------ .../rbacdef/RbacViewMermaidFlowchart.java | 48 ++++----- .../rbacdef/RbacViewPostgresGenerator.java | 2 +- 7 files changed, 231 insertions(+), 125 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java index 351a5f96..40b0a3d2 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java @@ -3,8 +3,6 @@ package net.hostsharing.hsadminng.hs.office.bankaccount; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; -import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; -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.stringify.Stringify; @@ -17,10 +15,8 @@ import jakarta.persistence.Table; import java.util.UUID; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*; -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.Role.*; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -59,7 +55,7 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable { return holder; } - public static RbacView hsOfficeBankAccount() { + public static RbacView rbac() { // @formatter:off return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class) .withIdentityView(SQL.query("target.iban || ':' || target.holder")) @@ -75,69 +71,4 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable { .pop(); // @formatter:on } - - public static RbacView hsOfficeDebitor() { - // @formatter:off - return rbacViewFor("debitor", HsOfficeDebitorEntity.class) - .withIdentityView(SQL.query(""" - SELECT debitor.uuid, - 'D-' || (SELECT partner.partnerNumber - FROM hs_office_partner partner - JOIN hs_office_relationship partnerRel - ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER' - JOIN hs_office_relationship debitorRel - ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' - WHERE debitorRel.uuid = debitor.debitorRelUuid) - || to_char(debitorNumberSuffix, 'fm00') - from hs_office_debitor as debitor; - """)) - .withUpdatableColumns( - "debitorRel", - "billable", - "billingContactUuid", - "refundBankAccountUuid", - "vatId", - "vatCountryCode", - "vatBusiness", - "vatreversecharge", - "defaultPrefix" /* TODO: do we want that updatable? */ ) - .createPermission(custom("new-debitor")).grantedTo("global", ADMIN).pop() - - .defineProxyEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, fetchedBySql(""" - SELECT * - FROM hs_office_relationship AS r - WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; - """), - dependsOnColumn("debitorRelUuid")) - .createPermission(ALL).grantedTo("debitorRel", OWNER).pop() - .createPermission(EDIT).grantedTo("debitorRel", ADMIN).pop() - .createPermission(VIEW).grantedTo("debitorRel", TENANT).pop() - - .defineEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, fetchedBySql(""" - SELECT * - FROM hs_office_relationship AS r - WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; - """), - dependsOnColumn("bankAccountUuid")) - .importRbacViewAs("refundBankAccount", HsOfficeBankAccountEntity.hsOfficeBankAccount()) - .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) - .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) - - .defineEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, fetchedBySql(""" - SELECT * - FROM hs_office_relationship AS partnerRel - WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid; - """), - dependsOnColumn("debitorRelUuid")) - .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) - .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT) - .toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT) - .declareEntityAliases("partnerPerson", "operationalPerson") - .forExampleRole("partnerPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN) - .forExampleRole("operationalPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN) - .forExampleRole("partnerRel", TENANT).wouldBeGrantedTo("partnerPerson", REFERRER); - - - // @formatter:on - } } 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 76480ac0..d4dbecda 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 @@ -4,8 +4,10 @@ import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; +import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; @@ -14,6 +16,13 @@ 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.Role.REFERRER; +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 @@ -97,4 +106,69 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { public String toShortString() { return DEBITOR_NUMBER_TAG + getDebitorNumberString(); } + + public static RbacView rbac() { + // @formatter:off + return rbacViewFor("debitor", HsOfficeDebitorEntity.class) + .withIdentityView(RbacView.SQL.query(""" + SELECT debitor.uuid, + 'D-' || (SELECT partner.partnerNumber + FROM hs_office_partner partner + JOIN hs_office_relationship partnerRel + ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER' + JOIN hs_office_relationship debitorRel + ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING' + WHERE debitorRel.uuid = debitor.debitorRelUuid) + || to_char(debitorNumberSuffix, 'fm00') + from hs_office_debitor as debitor; + """)) + .withUpdatableColumns( + "debitorRel", + "billable", + "billingContactUuid", + "refundBankAccountUuid", + "vatId", + "vatCountryCode", + "vatBusiness", + "vatReverseCharge", + "defaultPrefix" /* TODO: do we want that updatable? */ ) + .createPermission(custom("new-debitor")).grantedTo("global", ADMIN).pop() + + .importProxyEntity("debitorRel", HsOfficeRelationshipEntity.class, + fetchedBySql(""" + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + """), + dependsOnColumn("debitorRelUuid")) + .createPermission(ALL).grantedTo("debitorRel", OWNER).pop() + .createPermission(EDIT).grantedTo("debitorRel", ADMIN).pop() + .createPermission(VIEW).grantedTo("debitorRel", TENANT).pop() + + .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, + fetchedBySql(""" + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + """), + dependsOnColumn("bankAccountUuid")) + .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT) + .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER) + + .importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, + fetchedBySql(""" + SELECT * + FROM hs_office_relationship AS partnerRel + WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid; + """), + dependsOnColumn("debitorRelUuid")) + .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) + .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT) + .toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT) + .declarePlaceholderEntityAliases("partnerPerson", "operationalPerson") + .forExampleRole("partnerPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN) + .forExampleRole("operationalPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN) + .forExampleRole("partnerRel", TENANT).wouldBeGrantedTo("partnerPerson", REFERRER); + // @formatter:on + } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java index fde3972b..ad3cfc02 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java @@ -4,6 +4,7 @@ import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.persistence.HasUuid; +import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.apache.commons.lang3.StringUtils; @@ -11,6 +12,11 @@ import org.apache.commons.lang3.StringUtils; import jakarta.persistence.*; import java.util.UUID; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL; +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.query; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -56,4 +62,21 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable { return personType + " " + (!StringUtils.isEmpty(tradeName) ? tradeName : (familyName + ", " + givenName)); } + + public static RbacView rbac() { + // @formatter:off + return rbacViewFor("person", HsOfficePersonEntity.class) + .withIdentityView(query("concat(target.tradeName, target.familyName, target.givenName)")) + .withUpdatableColumns("personType", "tradeName", "givenName", "familyName") + .createRole(OWNER) + .withPermission(ALL) + .withCurrentUserAsOwner() + .withIncomingSuperRole(GLOBAL, ADMIN) + .createSubRole(ADMIN) + .withPermission(EDIT) + .createSubRole(REFERRER) + .withPermission(VIEW) + .pop(); + // @formatter:on + } } 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 704f2760..9a94279b 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 @@ -5,12 +5,20 @@ import lombok.experimental.FieldNameConstants; 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.rbac.rbacdef.RbacView; +import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import jakarta.persistence.*; 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.Permission.*; +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 @@ -67,4 +75,34 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable { public String toShortString() { return toShortString.apply(this); } + + public static RbacView rbac() { + // @formatter:off + return rbacViewFor("relationship", HsOfficeRelationshipEntity.class) + .withIdentityView(SQL.query(""" + (select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid) + || '-with-' || target.relType || '-' + || (select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid) + """)) + .withUpdatableColumns("contactUuid") + .importEntityAlias("anchorPerson", HsOfficePersonEntity.class, + fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid"), + dependsOnColumn("relAnchorUuid")) + .importEntityAlias("holderPerson", HsOfficePersonEntity.class, + fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid"), + dependsOnColumn("relHolderUuid")) + .createRole(OWNER) + .withCurrentUserAsOwner() + .withPermission(ALL) + .withIncomingSuperRole(GLOBAL, ADMIN) + .withIncomingSuperRole("anchorPerson", ADMIN) + .createSubRole(ADMIN) + .withPermission(EDIT) + .createSubRole(AGENT) + .withIncomingSuperRole("holderPerson", ADMIN) + .createSubRole(TENANT) + .withPermission(VIEW) + .pop(); + // @formatter:on + } } 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 1997696d..df377ab6 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti import net.hostsharing.hsadminng.persistence.HasUuid; import jakarta.validation.constraints.NotNull; +import java.lang.reflect.InvocationTargetException; import java.util.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; @@ -36,11 +37,11 @@ public class RbacView { private SQL identityViewSqlQuery; private EntityAlias entityAliasProxy; - public static RbacView rbacViewFor(final String alias, final Class entityClass) { + public static RbacView rbacViewFor(final String alias, final Class entityClass) { return new RbacView(alias, entityClass); } - RbacView(final String alias, final Class entityClass) { + RbacView(final String alias, final Class entityClass) { entityAlias = new EntityAlias(alias, entityClass); entityAliases.put(alias, entityAlias); new RbacUserReference(CREATOR); @@ -71,31 +72,53 @@ public class RbacView { return permDef; } - public RbacView declareEntityAliases(final String... aliasNames) { + public RbacView declarePlaceholderEntityAliases(final String... aliasNames) { for ( String alias: aliasNames ) { entityAliases.put(alias, new EntityAlias(alias)); } return this; } - public RbacView defineProxyEntityAlias( + public RbacView importProxyEntity( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { - entityAliasProxy = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum); - entityAliases.put(aliasName, entityAliasProxy); + if ( entityAliasProxy != null ) { + throw new IllegalStateException("there is already an entityAliasProxy: " + entityAliasProxy); + } + entityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum); return this; } - public RbacView defineEntityAlias( + public RbacView importEntityAlias( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { - entityAliases.put(aliasName, new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum)); + importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum); return this; } - public RbacView importRbacViewAs(final String aliasName, final RbacView importedRbacView) { + private EntityAlias importEntityAliasImpl(final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum) { + final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum); + entityAliases.put(aliasName, entityAlias); + try { + importAsAlias(aliasName, rbacDefinition(entityClass)); + } catch ( final Exception exc) { + new RuntimeException("cannot import entity: " + entityClass, exc); + } + return entityAlias; + } + + private static RbacView rbacDefinition(final Class entityClass) + throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { + return (RbacView) entityClass.getMethod("rbac").invoke(null); + } + + private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView) { final var mapper = new AliasNameMapper(importedRbacView, aliasName); - importedRbacView.getEntityAliases().values().forEach(entityAlias -> { - new EntityAlias( mapper.map(entityAlias.aliasName), entityAlias.entityClass); - }); + importedRbacView.getEntityAliases().values().stream() + .filter(entityAlias -> !importedRbacView.isMainEntityAlias(entityAlias)) + .filter(entityAlias -> !entityAlias.isGlobal()) + .forEach(entityAlias -> { + final String mappedAliasName = mapper.map(entityAlias.aliasName); + entityAliases.put(mappedAliasName, new EntityAlias(mappedAliasName, entityAlias.entityClass)); + }); importedRbacView.getRoleDefs().forEach(roleDef -> { new RbacRoleDefinition( findEntityAlias(mapper.map(roleDef.entityAlias.aliasName)), roleDef.role); }); @@ -120,15 +143,15 @@ public class RbacView { private RbacGrantDefinition grantRoleToCurrentUser(final RbacRoleDefinition roleDefinition) { - return new RbacGrantDefinition(roleDefinition, currentUser()); + return new RbacGrantDefinition(roleDefinition, currentUser()).toCreate(); } private RbacGrantDefinition grantPermissionToRole(final RbacPermissionDefinition permDef , final RbacRoleDefinition roleDef) { - return new RbacGrantDefinition(permDef, roleDef); + return new RbacGrantDefinition(permDef, roleDef).toCreate(); } private RbacGrantDefinition grantSubRoleToSuperRole(final RbacRoleDefinition subRoleDefinition, final RbacRoleDefinition superRoleDefinition) { - return new RbacGrantDefinition(subRoleDefinition, superRoleDefinition); + return new RbacGrantDefinition(subRoleDefinition, superRoleDefinition).toCreate(); } boolean isMainEntityAlias(final EntityAlias entityAlias) { @@ -148,7 +171,7 @@ public class RbacView { } public RbacView grantRole(final String entityAlias, final Role role) { - new RbacGrantDefinition(findRbacRole(entityAlias, role), superRoleDef); + new RbacGrantDefinition(findRbacRole(entityAlias, role), superRoleDef).toCreate(); return RbacView.this; } @@ -161,6 +184,7 @@ public class RbacView { private final RbacRoleDefinition superRoleDef; private final RbacRoleDefinition subRoleDef; private final RbacPermissionDefinition permDef; + private boolean toCreate; @Override public String toString() { @@ -203,7 +227,16 @@ public class RbacView { boolean isAssumed() { // TODO: not implemented yet - return false; + return true; + } + + boolean isToCreate() { + return toCreate; + } + + RbacGrantDefinition toCreate() { + toCreate = true; + return this; } public enum GrantType { @@ -262,7 +295,7 @@ public class RbacView { } public RbacPermissionDefinition grantedTo(final String entityAlias, final Role role) { - new RbacGrantDefinition(this, findRbacRole(entityAlias, role) ); + new RbacGrantDefinition(this, findRbacRole(entityAlias, role) ).toCreate(); return this; } @@ -309,7 +342,7 @@ public class RbacView { public RbacRoleDefinition createSubRole(final Role role) { final var roleDef = findRbacRole(entityAlias, role).toCreate(); - new RbacGrantDefinition(roleDef, this); + new RbacGrantDefinition(roleDef, this).toCreate(); return roleDef; } @@ -346,7 +379,7 @@ public class RbacView { } } - private EntityAlias findEntityAlias(final String aliasName) { + EntityAlias findEntityAlias(final String aliasName) { final var found = entityAliases.get(aliasName); if ( found == null ) { throw new IllegalArgumentException("entityAlias not found: " + aliasName); @@ -354,7 +387,7 @@ public class RbacView { return found; } - public RbacRoleDefinition findRbacRole(final EntityAlias entityAlias, final Role role) { + RbacRoleDefinition findRbacRole(final EntityAlias entityAlias, final Role role) { return roleDefs.stream() .filter(r -> r.getEntityAlias() == entityAlias && r.getRole().equals(role)) .findFirst() @@ -367,17 +400,21 @@ public class RbacView { record EntityAlias(String aliasName, Class entityClass, SQL fetchSql, Column dependsOnColum) { - public EntityAlias(final String aliasName) { - this(aliasName, null, null, null); - } + public EntityAlias(final String aliasName) { + this(aliasName, null, null, null); + } - public EntityAlias(final String aliasName, final Class entityClass) { - this(aliasName, entityClass, null, null); - } + public EntityAlias(final String aliasName, final Class entityClass) { + this(aliasName, entityClass, null, null); + } boolean isGlobal() { return aliasName().equals("global"); } + + boolean isPlaceholder() { + return entityClass == null; + } } public record Role(String roleName) { @@ -389,7 +426,7 @@ public class RbacView { @Override public String toString() { - return "." + roleName; + return ":" + roleName; } @Override @@ -409,7 +446,7 @@ public class RbacView { @Override public String toString() { - return "." + permission; + return ":" + permission; } } @@ -457,7 +494,10 @@ public class RbacView { if (originalAliasName.equals(importedRbacView.entityAlias.aliasName) ) { return outerAliasName; } - return originalAliasName; + if (originalAliasName.equals("global") ) { + return originalAliasName; + } + return outerAliasName + "." + originalAliasName; } } } 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 828c45ef..8a6e6ff7 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchart.java @@ -1,6 +1,6 @@ package net.hostsharing.hsadminng.rbac.rbacdef; -import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; +import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -11,7 +11,8 @@ import static java.util.stream.Collectors.joining; public class RbacViewMermaidFlowchart { - + public static final String HOSTSHARING_ORANGE = "#dd4901"; + public static final String HOSTSHARING_LIGHTBLUE = "#99bcdb"; private final RbacView rbacDef; private final StringBuilder flowchart = new StringBuilder(); @@ -21,38 +22,28 @@ public class RbacViewMermaidFlowchart { ### rbac %{entityAlias} %{timestamp} ```mermaid + %%{init:{'flowchart':{'htmlLabels':false}}}%% flowchart TB """ .replace("%{entityAlias}", rbacDef.getEntityAlias().aliasName()) .replace("%{timestamp}", LocalDateTime.now().toString())); - renderSubgraphGlobal(); renderEntitySubgraphs(); renderGrants(); flowchart.append("```"); } - - void renderSubgraphGlobal() { - flowchart.append(""" - subgraph global - style global fill: lightgray - - role:global.admin[global.admin] - end - """); - } - private void renderEntitySubgraphs() { rbacDef.getEntityAliases().values().stream() .filter(entityAlias -> !rbacDef.isEntityAliasProxy(entityAlias)) + .filter(entityAlias -> !entityAlias.isPlaceholder()) .forEach(this::renderEntitySubgraph); } private void renderEntitySubgraph(final RbacView.EntityAlias entity) { - final var color = rbacDef.isMainEntityAlias(entity) ? "lightgreen" : "lightgray"; + final var color = rbacDef.isMainEntityAlias(entity) ? HOSTSHARING_ORANGE : HOSTSHARING_LIGHTBLUE; flowchart.append(""" - subgraph %{aliasName} + subgraph %{aliasName}["`**%{aliasName}**`"] direction TB style %{aliasName} fill: %{color} @@ -98,13 +89,16 @@ public class RbacViewMermaidFlowchart { } private String grantDef(final RbacView.RbacGrantDefinition grant) { + final var arrow = grant.isToCreate() + ? grant.isAssumed() ? " ==> " : " == // ==> " + : grant.isAssumed() ? " -.-> " : " -.- // -.-> "; return switch (grant.grantType()) { case USER_TO_ROLE -> // TODO: other user types not implemented yet - "user:creator" + (grant.isAssumed() ? " -.-> " : " --> ") + roleId(grant.getSubRoleDef()); + "user:creator" + arrow + roleId(grant.getSubRoleDef()); case ROLE_TO_ROLE -> - roleId(grant.getSuperRoleDef()) + (grant.isAssumed() ? " -.-> " : " --> ") + roleId(grant.getSubRoleDef()); - case ROLE_TO_PERM -> roleId(grant.getSuperRoleDef()) + " --> " + permId(grant.getPermDef()); + roleId(grant.getSuperRoleDef()) + arrow + roleId(grant.getSubRoleDef()); + case ROLE_TO_PERM -> roleId(grant.getSuperRoleDef()) + arrow + permId(grant.getPermDef()); }; } @@ -131,14 +125,20 @@ public class RbacViewMermaidFlowchart { public static void main(String[] args) throws IOException { - Files.writeString( - Paths.get("doc", "hsOfficeBankAccount.md"), - new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.hsOfficeBankAccount()).toString(), - StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); +// Files.writeString( +// Paths.get("doc", "hsOfficeRelationship.md"), +// new RbacViewMermaidFlowchart(HsOfficeRelationshipEntity.rbac()).toString(), +// StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); +// +// +// Files.writeString( +// Paths.get("doc", "hsOfficeBankAccount.md"), +// new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.rbac()).toString(), +// StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); Files.writeString( Paths.get("doc", "hsOfficeDebitor.md"), - new RbacViewMermaidFlowchart(HsOfficeBankAccountEntity.hsOfficeDebitor()).toString(), + new RbacViewMermaidFlowchart(HsOfficeDebitorEntity.rbac()).toString(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } } 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 862c3458..6079ba3b 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java @@ -41,7 +41,7 @@ public static void main(String[] args) throws IOException { Files.writeString( Paths.get("doc", "hsOfficeBankAccount.sql"), - new RbacViewPostgresGenerator(HsOfficeBankAccountEntity.hsOfficeBankAccount()).toString(), + new RbacViewPostgresGenerator(HsOfficeBankAccountEntity.rbac()).toString(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } }