RBAC generator with conditional grants used for REPRESENTATIVE-Relation #33
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<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.
|
||||
*
|
||||
@ -277,6 +286,7 @@ public class RbacView {
|
||||
public <EC extends RbacObject> RbacView importRootEntityAliasProxy(
|
||||
final String aliasName,
|
||||
final Class<? extends RbacObject> 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<? extends RbacObject> entityClass,
|
||||
final Column dependsOnColum) {
|
||||
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
private EntityAlias importEntityAliasImpl(
|
||||
final String aliasName, final Class<? extends RbacObject> 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<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 {
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user