From 92086d8634d25c9e30b385ad8f78f5db0a656d6e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 3 Apr 2024 10:22:23 +0200 Subject: [PATCH] scribbled the DSL for switchOnColumn+inCaseOf --- .../office/debitor/HsOfficeDebitorEntity.java | 2 + .../office/partner/HsOfficePartnerEntity.java | 2 + .../relation/HsOfficeRelationEntity.java | 76 +++++++++++------- .../hsadminng/rbac/rbacdef/RbacView.java | 77 +++++++++++++++---- 4 files changed, 117 insertions(+), 40 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 08c70f66..8b48e221 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 @@ -26,6 +26,7 @@ import static jakarta.persistence.CascadeType.PERSIST; import static jakarta.persistence.CascadeType.REFRESH; import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase; 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.*; @@ -157,6 +158,7 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable { .toRole("global", ADMIN).grantPermission(INSERT) .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, + usingCase("DEBITOR"), directlyFetchedByDependsOnColumn(), dependsOnColumn("debitorRelUuid")) .createPermission(DELETE).grantedTo("debitorRel", OWNER) 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 43b78fca..3c5f1983 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 @@ -29,6 +29,7 @@ import java.util.UUID; import static jakarta.persistence.CascadeType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase; 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.Role.*; @@ -98,6 +99,7 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject { .toRole("global", ADMIN).grantPermission(INSERT) .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, + usingDefaultCase(), directlyFetchedByDependsOnColumn(), dependsOnColumn("partnerRelUuid")) .createPermission(DELETE).grantedTo("partnerRel", ADMIN) 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 8d6c6fe8..2202e366 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 @@ -11,17 +11,17 @@ import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import jakarta.persistence.*; +import jakarta.persistence.Column; import java.io.IOException; 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.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.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -101,31 +101,53 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable { dependsOnColumn("contactUuid"), directlyFetchedByDependsOnColumn(), NOT_NULL) - .createRole(OWNER, (with) -> { - with.owningUser(CREATOR); - with.incomingSuperRole(GLOBAL, ADMIN); - // TODO: if type=REPRESENTATIIVE - // with.incomingSuperRole("holderPerson", ADMIN); - with.permission(DELETE); - }) - .createSubRole(ADMIN, (with) -> { - with.incomingSuperRole("anchorPerson", ADMIN); - // TODO: if type=REPRESENTATIIVE - // with.outgoingSuperRole("anchorPerson", OWNER); - with.permission(UPDATE); - }) - .createSubRole(AGENT, (with) -> { - with.incomingSuperRole("holderPerson", ADMIN); - }) - .createSubRole(TENANT, (with) -> { - with.incomingSuperRole("holderPerson", ADMIN); - with.incomingSuperRole("contact", ADMIN); - with.outgoingSubRole("anchorPerson", REFERRER); - with.outgoingSubRole("holderPerson", REFERRER); - with.outgoingSubRole("contact", REFERRER); - with.permission(SELECT); - }) - + .switchOnColumn("type", + inCaseOf("REPRESENTATIVE", then -> { + then.createRole(OWNER, (with) -> { + with.owningUser(CREATOR); + with.incomingSuperRole(GLOBAL, ADMIN); + with.incomingSuperRole("holderPerson", ADMIN); + with.permission(DELETE); + }) + .createSubRole(ADMIN, (with) -> { + with.incomingSuperRole("anchorPerson", ADMIN); + with.outgoingSubRole("anchorPerson", OWNER); + with.permission(UPDATE); + }) + .createSubRole(AGENT, (with) -> { + }) + .createSubRole(TENANT, (with) -> { + with.incomingSuperRole("holderPerson", ADMIN); + with.incomingSuperRole("contact", ADMIN); + with.outgoingSubRole("anchorPerson", REFERRER); + with.outgoingSubRole("holderPerson", REFERRER); + with.outgoingSubRole("contact", REFERRER); + with.permission(SELECT); + }); + }), + //FIXME: .inCaseOf("DEBITOR") + inOtherCases(then -> { + then.createRole(OWNER, (with) -> { + with.owningUser(CREATOR); + with.incomingSuperRole(GLOBAL, ADMIN); + with.permission(DELETE); + }) + .createSubRole(ADMIN, (with) -> { + with.incomingSuperRole("anchorPerson", ADMIN); + with.permission(UPDATE); + }) + .createSubRole(AGENT, (with) -> { + with.incomingSuperRole("holderPerson", ADMIN); + }) + .createSubRole(TENANT, (with) -> { + with.incomingSuperRole("holderPerson", ADMIN); + with.incomingSuperRole("contact", ADMIN); + with.outgoingSubRole("anchorPerson", REFERRER); + with.outgoingSubRole("holderPerson", REFERRER); + with.outgoingSubRole("contact", REFERRER); + with.permission(SELECT); + }); + })) .toRole("anchorPerson", ADMIN).grantPermission(INSERT); } 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 d6c2d3cc..ab5e6b07 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -34,10 +34,10 @@ import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; import static org.apache.commons.lang3.StringUtils.uncapitalize; @Getter +// TODO.refa: rename to RbacDSL public class RbacView { public static final String GLOBAL = "global"; @@ -65,6 +65,15 @@ public class RbacView { private SQL orderBySqlExpression; private EntityAlias rootEntityAliasProxy; private RbacRoleDefinition previousRoleDef; + private final Map cases = new HashMap<>() { + @Override + public String put(final String key, final String value) { + if (containsKey(key)) { + throw new IllegalArgumentException("duplicate case: " + key); + } + return super.put(key, value); + } + }; /** Crates an RBAC definition template for the given entity class and defining the given alias. * @@ -277,6 +286,7 @@ public class RbacView { public RbacView importRootEntityAliasProxy( final String aliasName, final Class entityClass, + final ColumnValue forCase, final SQL fetchSql, final Column dependsOnColum) { if (rootEntityAliasProxy != null) { @@ -339,14 +349,6 @@ public class RbacView { 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) { - importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null); - return this; - } - private EntityAlias importEntityAliasImpl( final String aliasName, final Class entityClass, final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { @@ -397,6 +399,12 @@ public class RbacView { return this; } + public RbacView switchOnColumn(final String discriminatorColumName, final CaseDef... caseDefs) { + // FIXME: currently only the default case is executed + stream(caseDefs).filter(caseDef -> caseDef.val == null).findAny().orElseThrow().def.accept(this); + return this; + } + private void verifyVersionColumnExists() { if (stream(rootEntityAlias.entityClass.getDeclaredFields()) .noneMatch(f -> f.getAnnotation(Version.class) != null)) { @@ -480,6 +488,25 @@ public class RbacView { } + public static class CaseDef { + + private final String val; + private final Consumer def; + + public CaseDef(final String discriminatorColumnValue, final Consumer def) { + this.val = discriminatorColumnValue; + this.def = def; + } + } + + public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer def) { + return new CaseDef(discriminatorColumnValue, def); + } + + public static CaseDef inOtherCases(final Consumer def) { + return new CaseDef(null, def); + } + public enum Nullable { NOT_NULL, // DEFAULT NULLABLE @@ -495,7 +522,8 @@ public class RbacView { private final RbacPermissionDefinition permDef; private boolean assumed = true; private boolean toCreate = false; - private String sqlWhere; + private String onlyInCaseOf; + private String exceptInCaseOf; @Override public String toString() { @@ -549,7 +577,7 @@ public class RbacView { } boolean isConditional() { - return sqlWhere != null; + return onlyInCaseOf != null; } boolean isToCreate() { @@ -578,8 +606,14 @@ public class RbacView { return this; } - public RbacGrantDefinition where(final String sqlWhere) { - this.sqlWhere = sqlWhere; + public RbacGrantDefinition onlyInCaseOf(final String caseName) { + this.onlyInCaseOf = caseName; + return this; + } + + + public RbacGrantDefinition exceptInCaseOf(final String caseName) { + this.exceptInCaseOf = caseName; return this; } @@ -1034,6 +1068,23 @@ public class RbacView { } } + public static class ColumnValue { + + public static ColumnValue usingDefaultCase() { + return new ColumnValue(null); + } + + public static ColumnValue usingCase(final String value) { + return new ColumnValue(value); + } + + public final String value; + + private ColumnValue(final String value) { + this.value = value; + } + } + private static class AliasNameMapper { private final RbacView importedRbacView;