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 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)
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user