scribbled the DSL for switchOnColumn+inCaseOf

This commit is contained in:
Michael Hoennig 2024-04-03 10:22:23 +02:00
parent b5ef54e83c
commit 92086d8634
4 changed files with 117 additions and 40 deletions

View File

@ -26,6 +26,7 @@ import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH; import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; 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.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE; 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.Permission.*;
@ -157,6 +158,7 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
usingCase("DEBITOR"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid")) dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER) .createPermission(DELETE).grantedTo("debitorRel", OWNER)

View File

@ -29,6 +29,7 @@ import java.util.UUID;
import static jakarta.persistence.CascadeType.*; import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; 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.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -98,6 +99,7 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
.toRole("global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
usingDefaultCase(),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid")) dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN) .createPermission(DELETE).grantedTo("partnerRel", ADMIN)

View File

@ -11,17 +11,17 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.persistence.Column;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; 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.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.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; 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.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; 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.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@ -101,31 +101,53 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
dependsOnColumn("contactUuid"), dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NOT_NULL) NOT_NULL)
.createRole(OWNER, (with) -> { .switchOnColumn("type",
with.owningUser(CREATOR); inCaseOf("REPRESENTATIVE", then -> {
with.incomingSuperRole(GLOBAL, ADMIN); then.createRole(OWNER, (with) -> {
// TODO: if type=REPRESENTATIIVE with.owningUser(CREATOR);
// with.incomingSuperRole("holderPerson", ADMIN); with.incomingSuperRole(GLOBAL, ADMIN);
with.permission(DELETE); with.incomingSuperRole("holderPerson", ADMIN);
}) with.permission(DELETE);
.createSubRole(ADMIN, (with) -> { })
with.incomingSuperRole("anchorPerson", ADMIN); .createSubRole(ADMIN, (with) -> {
// TODO: if type=REPRESENTATIIVE with.incomingSuperRole("anchorPerson", ADMIN);
// with.outgoingSuperRole("anchorPerson", OWNER); with.outgoingSubRole("anchorPerson", OWNER);
with.permission(UPDATE); with.permission(UPDATE);
}) })
.createSubRole(AGENT, (with) -> { .createSubRole(AGENT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN); })
}) .createSubRole(TENANT, (with) -> {
.createSubRole(TENANT, (with) -> { with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("holderPerson", ADMIN); with.incomingSuperRole("contact", ADMIN);
with.incomingSuperRole("contact", ADMIN); with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("anchorPerson", REFERRER); with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER); with.outgoingSubRole("contact", REFERRER);
with.outgoingSubRole("contact", REFERRER); with.permission(SELECT);
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); .toRole("anchorPerson", ADMIN).grantPermission(INSERT);
} }

View File

@ -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.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; 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.Part.AUTO_FETCH;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@Getter @Getter
// TODO.refa: rename to RbacDSL
public class RbacView { public class RbacView {
public static final String GLOBAL = "global"; public static final String GLOBAL = "global";
@ -65,6 +65,15 @@ public class RbacView {
private SQL orderBySqlExpression; private SQL orderBySqlExpression;
private EntityAlias rootEntityAliasProxy; private EntityAlias rootEntityAliasProxy;
private RbacRoleDefinition previousRoleDef; private RbacRoleDefinition previousRoleDef;
private final Map<String, String> 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. /** Crates an RBAC definition template for the given entity class and defining the given alias.
* *
@ -277,6 +286,7 @@ public class RbacView {
public <EC extends RbacObject> RbacView importRootEntityAliasProxy( public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
final String aliasName, final String aliasName,
final Class<? extends RbacObject> entityClass, final Class<? extends RbacObject> entityClass,
final ColumnValue forCase,
final SQL fetchSql, final SQL fetchSql,
final Column dependsOnColum) { final Column dependsOnColum) {
if (rootEntityAliasProxy != null) { if (rootEntityAliasProxy != null) {
@ -339,14 +349,6 @@ public class RbacView {
return this; return this;
} }
// TODO: remove once it's not used in HsOffice...Entity anymore
public RbacView importEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass,
final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this;
}
private EntityAlias importEntityAliasImpl( private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends RbacObject> entityClass, final String aliasName, final Class<? extends RbacObject> entityClass,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
@ -397,6 +399,12 @@ public class RbacView {
return this; 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() { private void verifyVersionColumnExists() {
if (stream(rootEntityAlias.entityClass.getDeclaredFields()) if (stream(rootEntityAlias.entityClass.getDeclaredFields())
.noneMatch(f -> f.getAnnotation(Version.class) != null)) { .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<RbacView> def;
public CaseDef(final String discriminatorColumnValue, final Consumer<RbacView> def) {
this.val = discriminatorColumnValue;
this.def = def;
}
}
public static CaseDef inCaseOf(final String discriminatorColumnValue, final Consumer<RbacView> def) {
return new CaseDef(discriminatorColumnValue, def);
}
public static CaseDef inOtherCases(final Consumer<RbacView> def) {
return new CaseDef(null, def);
}
public enum Nullable { public enum Nullable {
NOT_NULL, // DEFAULT NOT_NULL, // DEFAULT
NULLABLE NULLABLE
@ -495,7 +522,8 @@ public class RbacView {
private final RbacPermissionDefinition permDef; private final RbacPermissionDefinition permDef;
private boolean assumed = true; private boolean assumed = true;
private boolean toCreate = false; private boolean toCreate = false;
private String sqlWhere; private String onlyInCaseOf;
private String exceptInCaseOf;
@Override @Override
public String toString() { public String toString() {
@ -549,7 +577,7 @@ public class RbacView {
} }
boolean isConditional() { boolean isConditional() {
return sqlWhere != null; return onlyInCaseOf != null;
} }
boolean isToCreate() { boolean isToCreate() {
@ -578,8 +606,14 @@ public class RbacView {
return this; return this;
} }
public RbacGrantDefinition where(final String sqlWhere) { public RbacGrantDefinition onlyInCaseOf(final String caseName) {
this.sqlWhere = sqlWhere; this.onlyInCaseOf = caseName;
return this;
}
public RbacGrantDefinition exceptInCaseOf(final String caseName) {
this.exceptInCaseOf = caseName;
return this; 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 static class AliasNameMapper {
private final RbacView importedRbacView; private final RbacView importedRbacView;