From 491516e516507d7003bd48c8b052dd315d6ce724 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 21 Feb 2024 13:02:54 +0100 Subject: [PATCH] experimental RbacView: API for a potential Mermaid + pl/pgSQL generator --- .../HsOfficeBankAccountEntity.java | 88 +++++++++++ .../hsadminng/rbac/rbacdef/RbacView.java | 147 ++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java 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 4d067f68..94cd60e4 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,7 +3,10 @@ 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; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -13,6 +16,10 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; 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.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -50,4 +57,85 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable { public String toShortString() { return holder; } + + public static RbacView hsOfficeBankAccount() { + // @formatter:off + return rbacViewFor(HsOfficeBankAccountEntity.class) + .alias("bankAccount") + .withIdentityViewSqlQuery("target.iban || ':' || target.holder") + .withUpdatableColumns("holder", "iban", "bic") + .createRole(OWNER) + .withCurrentUserAsOwner() + .withPermission(ALL) + .withIncomingSuperRole(GLOBAL, ADMIN) + .createSubRole(ADMIN) + .withPermission(UPDATE) + .createSubRole(REFERRER) + .withPermission(READ) + .pop(); + // @formatter:on + } + + public static RbacView hsOfficeDebitor() { + // @formatter:off + return rbacViewFor(HsOfficeDebitorEntity.class) + .alias("debitor") + .withIdentityViewSqlQuery(""" + 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(extraPermission("new-debitor")).grantedTo("global", ADMIN).pop() + + .defineEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, """ + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + """, "debitorRelUuid") + .createPermission(ALL).grantedTo("hsOfficeRelationship:DEBITOR", OWNER).pop() + .createPermission(UPDATE).grantedTo("hsOfficeRelationship:DEBITOR", ADMIN).pop() + .createPermission(READ).grantedTo("hsOfficeRelationship:DEBITOR", TENANT).pop() + + .defineEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, """ + SELECT * + FROM hs_office_relationship AS r + WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid; + """, "bankAccountUuid") + .toRole("hsOfficeBankAccount", ADMIN).grantRole("debitorRel", AGENT) + .toRole("debitorRel", AGENT).grantRole("hsOfficeBankAccount", REFERRER) + + .defineEntityAlias("partnerRel", HsOfficeRelationshipEntity.class, """ + SELECT * + FROM hs_office_relationship AS partnerRel + WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid; + """, "debitorRelUuid") + .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN) + .toRole("debitorRel", ADMIN).grantRole("partnerRel", AGENT) + .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/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java new file mode 100644 index 00000000..1b6af664 --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -0,0 +1,147 @@ +package net.hostsharing.hsadminng.rbac.rbacdef; + +import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; +import net.hostsharing.hsadminng.persistence.HasUuid; + +public class RbacView { + + public static final String GLOBAL = "global"; + + public static RbacView rbacViewFor(final Class entityClass) { + return new RbacView<>(entityClass); + } + + RbacView(final Class entityClass) { + + } + public RbacView alias(final String bankAccount) { + return this; + } + + public RbacView withUpdatableColumns(final String... columnNames) { + return this; + } + + public RbacView withIdentityViewSqlQuery(final String sqlExpression) { + return this; + } + + public RbacRoleDefinition createRole(final Role role) { + return new RbacRoleDefinition<>(role); + } + + public RbacPermissionDefinition createPermission(final Permission permission) { + return new RbacPermissionDefinition<>(permission); + } + + public RbacView declareEntityAliases(final String... aliases) { + return this; + } + + public RbacView defineEntityAlias( + final String alias, final Class entityClass, final String fetchSql, final String dependsOnColum) { + return this; + } + + public RbacRole toRole(final String hsOfficeBankAccount, final Role role) { + return new RbacRole(hsOfficeBankAccount, role); + } + + public RbacExampleRole forExampleRole(final String entityAlias, final Role role) { + return new RbacExampleRole(entityAlias, role); + } + + public class RbacRole { + + public RbacRole(final String entityAlias, final Role role) { + } + + public RbacView grantRole(final String entityAlias, final Role role) { + return RbacView.this; + } + } + + public class RbacExampleRole { + + public RbacExampleRole(final String entityAlias, final Role role) { + } + + public RbacView wouldBeGrantedTo(final String entityAlias, final Role role) { + return RbacView.this; + } + } + + public class RbacPermissionDefinition { + + public RbacPermissionDefinition(final Permission permission) { + } + + public RbacView pop() { + return RbacView.this; + } + + public RbacPermissionDefinition withIncomingSuperRole( + final Class hsOfficeRelationshipEntityClass, + final Role owner) { + return this; + } + + public RbacPermissionDefinition grantedTo(final String entityAlias, final Role owner) { + return this; + } + } + + public class RbacRoleDefinition { + + public RbacRoleDefinition(final Role role) { + } + + public RbacRoleDefinition withCurrentUserAsOwner() { + return this; + } + + public RbacRoleDefinition withPermission(final Permission permission) { + return this; + } + + public RbacRoleDefinition withIncomingSuperRole(final String tableName, final Role role) { + return this; + } + + public RbacRoleDefinition createSubRole(final Role role) { + return this; + } + + public RbacView pop() { + return RbacView.this; + } + } + + public static class Role { + public static final Role OWNER = new Role("owner"); + public static final Role ADMIN = new Role("admin"); + public static final Role AGENT = new Role("agent"); + public static final Role TENANT = new Role("tenant"); + public static final Role REFERRER = new Role("referrer"); + + public Role(final String roleName) { + + } + } + + public static class Permission { + public static final Permission ALL = new Permission("*"); + public static final Permission UPDATE = new Permission("edit"); + public static final Permission READ = new Permission("view"); + + public static Permission extraPermission(final String permission) { + return new Permission(permission); + } + + final String permission; + + private Permission(final String permission) { + this.permission = permission; + } + } +}