conditional insert permission grant (so far just exactly 1 unique for each table) (#48)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: #48
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-04-23 10:42:24 +02:00
parent 4eda99b95a
commit 9806bcd78f
20 changed files with 111 additions and 89 deletions

View File

@ -174,7 +174,7 @@ project.tasks.processResources.dependsOn processSpring
project.tasks.compileJava.dependsOn processSpring
// Rename javax to jakarta in OpenApi generated java files because
// io.openapiprocessor.openapi-processor 2022.2 does not yet support the openapiprocessor useSpringBoot3 config option.
// io.openapiprocessor.openapi-processor 2022.5 does not yet support the openapiprocessor useSpringBoot3 config option.
// TODO.impl: Upgrade to io.openapiprocessor.openapi-processor >= 2024.2
// and use either `bean-validation: true` in api-mapping.yaml or `useSpringBoot3 true` (not sure where exactly).
task openApiGenerate(type: Copy) {

View File

@ -35,10 +35,13 @@ import java.util.Map;
import java.util.UUID;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
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.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
@ -148,12 +151,12 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject {
.withRestrictedViewOrderBy(SQL.expression("validity"))
.withUpdatableColumns("version", "caption", "validity", "resources")
.importEntityAlias("debitor", HsOfficeDebitorEntity.class,
.importEntityAlias("debitor", HsOfficeDebitorEntity.class, usingDefaultCase(),
dependsOnColumn("debitorUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class, usingCase(DEBITOR),
dependsOnColumn("debitorUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -22,6 +22,7 @@ import java.util.UUID;
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.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
@ -125,7 +126,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacO
return rbacViewFor("coopAssetsTransaction", HsOfficeCoopAssetsTransactionEntity.class)
.withIdentityView(RbacView.SQL.projection("reference"))
.withUpdatableColumns("comment")
.importEntityAlias("membership", HsOfficeMembershipEntity.class,
.importEntityAlias("membership", HsOfficeMembershipEntity.class, usingDefaultCase(),
dependsOnColumn("membershipUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -20,6 +20,7 @@ import java.util.UUID;
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.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
@ -119,7 +120,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacO
return rbacViewFor("coopSharesTransaction", HsOfficeCoopSharesTransactionEntity.class)
.withIdentityView(SQL.projection("reference"))
.withUpdatableColumns("comment")
.importEntityAlias("membership", HsOfficeMembershipEntity.class,
.importEntityAlias("membership", HsOfficeMembershipEntity.class, usingDefaultCase(),
dependsOnColumn("membershipUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -36,7 +36,9 @@ import static jakarta.persistence.CascadeType.MERGE;
import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
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.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
@ -171,23 +173,21 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
"defaultPrefix" /* TODO.spec: do we want that updatable? */)
.toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
// TODO.spec: do we need a distinct case for DEBITOR-Relation?
usingDefaultCase(),
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class, usingCase(DEBITOR),
directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class, usingDefaultCase(),
dependsOnColumn("refundBankAccountUuid"),
directlyFetchedByDependsOnColumn(),
NULLABLE)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, usingDefaultCase(),
dependsOnColumn("debitorRelUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -38,6 +38,7 @@ import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveF
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
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.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
@ -156,7 +157,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
.withRestrictedViewOrderBy(SQL.projection("validity"))
.withUpdatableColumns("validity", "membershipFeeBillable", "status")
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, usingDefaultCase(),
dependsOnColumn("partnerUuid"),
fetchedBySql("""
SELECT ${columns}

View File

@ -19,6 +19,8 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inOtherCases;
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.ColumnValue.usingDefaultCase;
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;
@ -94,15 +96,15 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
.withRestrictedViewOrderBy(SQL.expression(
"(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class, usingDefaultCase(),
dependsOnColumn("anchorUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
.importEntityAlias("holderPerson", HsOfficePersonEntity.class, usingDefaultCase(),
dependsOnColumn("holderUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)
.importEntityAlias("contact", HsOfficeContactEntity.class,
.importEntityAlias("contact", HsOfficeContactEntity.class, usingDefaultCase(),
dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -18,8 +18,11 @@ import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
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.ColumnValue.usingDefaultCase;
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.*;
@ -107,7 +110,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject {
.withRestrictedViewOrderBy(expression("validity"))
.withUpdatableColumns("reference", "agreement", "validity")
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
.importEntityAlias("debitorRel", HsOfficeRelationEntity.class, usingCase(DEBITOR),
dependsOnColumn("debitorUuid"),
fetchedBySql("""
SELECT ${columns}
@ -116,7 +119,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject {
WHERE debitor.uuid = ${REF}.debitorUuid
"""),
NOT_NULL)
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class,
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, usingDefaultCase(),
dependsOnColumn("bankAccountUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -50,7 +50,7 @@ public class InsertTriggerGenerator {
begin
call defineContext('create INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows');
FOR row IN SELECT * FROM ${rawSuperTableName}
FOR row IN SELECT * FROM ${rawSuperTableName}${typeCondition}
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', '${rawSubTableName}'),
@ -61,7 +61,10 @@ public class InsertTriggerGenerator {
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row")),
with("typeCondition", superRoleDef.getEntityAlias().isCaseDependent()
? "\n\t\t\tWHERE type = '${case}'".replace("${case}", superRoleDef.getEntityAlias().usingCase().value)
: "")
);
});
}
@ -77,9 +80,9 @@ public class InsertTriggerGenerator {
language plpgsql
strict as $$
begin
call grantPermissionToRole(
${typeConditionIf}call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'),
${rawSuperRoleDescriptor});
${rawSuperRoleDescriptor});${typeConditionEndIf}
return NEW;
end; $$;
@ -91,7 +94,14 @@ public class InsertTriggerGenerator {
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name()))
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name())),
with("typeConditionIf",
superRoleDef.getEntityAlias().isCaseDependent()
? "if NEW.type = '${case}' then\n\t\t".replace("${case}", superRoleDef.getEntityAlias().usingCase().value)
: ""),
with("typeConditionEndIf", superRoleDef.getEntityAlias().isCaseDependent()
? "\n\tend if;"
: "")
);
});
}
@ -241,7 +251,10 @@ public class InsertTriggerGenerator {
private static <T> BinaryOperator<T> singleton() {
return (x, y) -> {
throw new IllegalStateException("only a single INSERT permission grant allowed");
if ( !x.equals(y) ) {
throw new IllegalStateException("only a single INSERT permission grant allowed");
}
return x;
};
}

View File

@ -18,7 +18,9 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import static java.lang.reflect.Modifier.isStatic;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Collections.max;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
@ -325,6 +327,9 @@ public class RbacView {
* A JPA entity class extending RbacObject which also implements an `rbac` method returning
* its RBAC specification.
*
* @param usingCase
* Only use this case value for a switch within the rbac rules.
*
* @param fetchSql
* An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
* newly created or updated row (will be replaced by NEW/OLD from the trigger method).
@ -342,19 +347,29 @@ public class RbacView {
* a JPA entity class extending RbacObject
*/
public RbacView importEntityAlias(
final String aliasName, final Class<? extends RbacObject> entityClass,
final String aliasName, final Class<? extends RbacObject> entityClass, final ColumnValue usingCase,
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, false, nullable);
importEntityAliasImpl(aliasName, entityClass, usingCase, fetchSql, dependsOnColum, false, nullable);
return this;
}
private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends RbacObject> entityClass, final ColumnValue forCase,
final String aliasName, final Class<? extends RbacObject> entityClass, final ColumnValue usingCase,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, entityAlias);
final var entityAlias = ofNullable(entityAliases.get(aliasName))
.orElseGet(() -> {
final var ea = new EntityAlias(aliasName, entityClass, usingCase, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, ea);
return ea;
});
try {
importAsAlias(aliasName, rbacDefinition(entityClass), forCase, asSubEntity);
// TODO.rbac: this only works for directly recursive RBAC definitions, not for indirect recursion
final var rbacDef = entityClass == rootEntityAlias.entityClass
? this
: rbacDefinition(entityClass);
importAsAlias(aliasName, rbacDef, usingCase, asSubEntity);
} catch (final ReflectiveOperationException exc) {
throw new RuntimeException("cannot import entity: " + entityClass, exc);
}
@ -369,7 +384,7 @@ public class RbacView {
private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final ColumnValue forCase, final boolean asSubEntity) {
final var mapper = new AliasNameMapper(importedRbacView, aliasName,
asSubEntity ? entityAliases.keySet() : null);
importedRbacView.getEntityAliases().values().stream()
copyOf(importedRbacView.getEntityAliases().values()).stream()
.filter(entityAlias -> !importedRbacView.isRootEntityAlias(entityAlias))
.filter(entityAlias -> !entityAlias.isGlobal())
.filter(entityAlias -> !asSubEntity || !entityAliases.containsKey(entityAlias.aliasName))
@ -377,10 +392,10 @@ public class RbacView {
final String mappedAliasName = mapper.map(entityAlias.aliasName);
entityAliases.put(mappedAliasName, new EntityAlias(mappedAliasName, entityAlias.entityClass));
});
importedRbacView.getRoleDefs().forEach(roleDef -> {
copyOf(importedRbacView.getRoleDefs()).forEach(roleDef -> {
new RbacRoleDefinition(findEntityAlias(mapper.map(roleDef.entityAlias.aliasName)), roleDef.role);
});
importedRbacView.getGrantDefs().forEach(grantDef -> {
copyOf(importedRbacView.getGrantDefs()).forEach(grantDef -> {
if ( grantDef.grantType() == RbacGrantDefinition.GrantType.ROLE_TO_ROLE &&
(grantDef.forCases == null || grantDef.matchesCase(forCase)) ) {
final var importedGrantDef = findOrCreateGrantDef(
@ -411,6 +426,10 @@ public class RbacView {
return this;
}
private static <T> List<T> copyOf(final Collection<T> eas) {
return eas.stream().toList();
}
private void verifyVersionColumnExists() {
if (stream(rootEntityAlias.entityClass.getDeclaredFields())
.noneMatch(f -> f.getAnnotation(Version.class) != null)) {
@ -615,6 +634,13 @@ public class RbacView {
return this;
}
public long level() {
return max(asList(
superRoleDef != null ? superRoleDef.entityAlias.level() : 0,
subRoleDef != null ? subRoleDef.entityAlias.level() : 0,
permDef != null ? permDef.entityAlias.level() : 0));
}
public enum GrantType {
ROLE_TO_USER,
ROLE_TO_ROLE,
@ -854,14 +880,14 @@ public class RbacView {
return distinctGrantDef;
}
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
record EntityAlias(String aliasName, Class<? extends RbacObject> entityClass, ColumnValue usingCase, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
public EntityAlias(final String aliasName) {
this(aliasName, null, null, null, false, null);
this(aliasName, null, null, null, null, false, null);
}
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
this(aliasName, entityClass, null, null, false, null);
this(aliasName, entityClass, null, null, null, false, null);
}
boolean isGlobal() {
@ -873,7 +899,6 @@ public class RbacView {
}
@NotNull
@Override
public SQL fetchSql() {
if (fetchSql == null) {
return SQL.noop();
@ -914,6 +939,14 @@ public class RbacView {
}
return dependsOnColum.column;
}
long level() {
return aliasName.chars().filter(ch -> ch == '.').count() + 1;
}
boolean isCaseDependent() {
return usingCase != null && usingCase.value != null;
}
}
public static String withoutRvSuffix(final String tableName) {
@ -1074,10 +1107,9 @@ public class RbacView {
return new ColumnValue(null);
}
public static ColumnValue usingCase(final String value) {
return new ColumnValue(value);
public static <E extends Enum<E>> ColumnValue usingCase(final E value) {
return new ColumnValue(value.name());
}
public final String value;
private ColumnValue(final String value) {

View File

@ -15,6 +15,9 @@ public class RbacViewMermaidFlowchartGenerator {
public static final String HOSTSHARING_LIGHT_ORANGE = "#feb28c";
public static final String HOSTSHARING_DARK_BLUE = "#274d6e";
public static final String HOSTSHARING_LIGHT_BLUE = "#99bcdb";
// TODO.rbac: implement level limit for all renderable items and remove items which not part of a grant
private static final long MAX_LEVEL_TO_RENDER = 3;
private final RbacView rbacDef;
private final CaseDef forCase;
@ -56,6 +59,7 @@ public class RbacViewMermaidFlowchartGenerator {
flowchart.indented( () -> {
rbacDef.getEntityAliases().values().stream()
.filter(e -> e.level() <= MAX_LEVEL_TO_RENDER)
.filter(e -> e.aliasName().startsWith(entity.aliasName() + ":"))
.forEach(this::renderEntitySubgraph);
@ -106,6 +110,7 @@ public class RbacViewMermaidFlowchartGenerator {
private void renderGrants(final RbacView.RbacGrantDefinition.GrantType grantType, final String comment) {
final var grantsOfRequestedType = rbacDef.getGrantDefs().stream()
.filter(g -> g.level() <= MAX_LEVEL_TO_RENDER)
.filter(g -> g.grantType() == grantType)
.filter(this::isToBeRenderedInThisGraph)
.toList();

View File

@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.UUID;
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.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -48,7 +49,7 @@ public class TestDomainEntity implements RbacObject {
.withIdentityView(SQL.projection("name"))
.withUpdatableColumns("version", "packageUuid", "description")
.importEntityAlias("package", TestPackageEntity.class,
.importEntityAlias("package", TestPackageEntity.class, usingDefaultCase(),
dependsOnColumn("packageUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.UUID;
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.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@ -49,7 +50,7 @@ public class TestPackageEntity implements RbacObject {
.withIdentityView(SQL.projection("name"))
.withUpdatableColumns("version", "customerUuid", "description")
.importEntityAlias("customer", TestCustomerEntity.class,
.importEntityAlias("customer", TestCustomerEntity.class, usingDefaultCase(),
dependsOnColumn("customerUuid"),
directlyFetchedByDependsOnColumn(),
NOT_NULL)

View File

@ -149,16 +149,6 @@ role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel.holderPerson:REFERRER
role:global:ADMIN -.-> role:debitorRel.contact:OWNER
role:debitorRel.contact:OWNER -.-> role:debitorRel.contact:ADMIN
role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel:OWNER -.-> role:debitorRel:ADMIN
role:debitorRel:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT -.-> role:debitorRel:TENANT
role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:global:ADMIN -.-> role:refundBankAccount:OWNER
role:refundBankAccount:OWNER -.-> role:refundBankAccount:ADMIN
role:refundBankAccount:ADMIN -.-> role:refundBankAccount:REFERRER

View File

@ -108,16 +108,6 @@ role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel.holderPerson:REFERRER
role:global:ADMIN -.-> role:debitorRel.contact:OWNER
role:debitorRel.contact:OWNER -.-> role:debitorRel.contact:ADMIN
role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel:OWNER -.-> role:debitorRel:ADMIN
role:debitorRel:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT -.-> role:debitorRel:TENANT
role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:global:ADMIN -.-> role:bankAccount:OWNER
role:bankAccount:OWNER -.-> role:bankAccount:ADMIN
role:bankAccount:ADMIN -.-> role:bankAccount:REFERRER

View File

@ -115,6 +115,7 @@ do language plpgsql $$
call defineContext('create INSERT INTO hs_office_sepamandate permissions for the related hs_office_relation rows');
FOR row IN SELECT * FROM hs_office_relation
WHERE type = 'DEBITOR'
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_sepamandate'),
@ -131,9 +132,11 @@ create or replace function hs_office_sepamandate_hs_office_relation_insert_tf()
language plpgsql
strict as $$
begin
call grantPermissionToRole(
if NEW.type = 'DEBITOR' then
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_sepamandate'),
hsOfficeRelationADMIN(NEW));
end if;
return NEW;
end; $$;

View File

@ -216,16 +216,6 @@ role:debitor.debitorRel.holderPerson:ADMIN -.-> role:debitor.debitorRel.holderPe
role:global:ADMIN -.-> role:debitor.debitorRel.contact:OWNER
role:debitor.debitorRel.contact:OWNER -.-> role:debitor.debitorRel.contact:ADMIN
role:debitor.debitorRel.contact:ADMIN -.-> role:debitor.debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitor.debitorRel:OWNER
role:debitor.debitorRel:OWNER -.-> role:debitor.debitorRel:ADMIN
role:debitor.debitorRel:ADMIN -.-> role:debitor.debitorRel:AGENT
role:debitor.debitorRel:AGENT -.-> role:debitor.debitorRel:TENANT
role:debitor.debitorRel.contact:ADMIN -.-> role:debitor.debitorRel:TENANT
role:debitor.debitorRel:TENANT -.-> role:debitor.debitorRel.anchorPerson:REFERRER
role:debitor.debitorRel:TENANT -.-> role:debitor.debitorRel.holderPerson:REFERRER
role:debitor.debitorRel:TENANT -.-> role:debitor.debitorRel.contact:REFERRER
role:debitor.debitorRel.anchorPerson:ADMIN -.-> role:debitor.debitorRel:OWNER
role:debitor.debitorRel.holderPerson:ADMIN -.-> role:debitor.debitorRel:AGENT
role:global:ADMIN -.-> role:debitor.refundBankAccount:OWNER
role:debitor.refundBankAccount:OWNER -.-> role:debitor.refundBankAccount:ADMIN
role:debitor.refundBankAccount:ADMIN -.-> role:debitor.refundBankAccount:REFERRER
@ -262,16 +252,6 @@ role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel.holderPerson:REFERRER
role:global:ADMIN -.-> role:debitorRel.contact:OWNER
role:debitorRel.contact:OWNER -.-> role:debitorRel.contact:ADMIN
role:debitorRel.contact:ADMIN -.-> role:debitorRel.contact:REFERRER
role:global:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel:OWNER -.-> role:debitorRel:ADMIN
role:debitorRel:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT -.-> role:debitorRel:TENANT
role:debitorRel.contact:ADMIN -.-> role:debitorRel:TENANT
role:debitorRel:TENANT -.-> role:debitorRel.anchorPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.holderPerson:REFERRER
role:debitorRel:TENANT -.-> role:debitorRel.contact:REFERRER
role:debitorRel.anchorPerson:ADMIN -.-> role:debitorRel:OWNER
role:debitorRel.holderPerson:ADMIN -.-> role:debitorRel:AGENT
role:debitorRel:AGENT ==> role:bookingItem:OWNER
role:bookingItem:OWNER ==> role:bookingItem:ADMIN
role:debitorRel:AGENT ==> role:bookingItem:ADMIN

View File

@ -111,7 +111,7 @@ do language plpgsql $$
call defineContext('create INSERT INTO hs_booking_item permissions for the related hs_office_relation rows');
FOR row IN SELECT * FROM hs_office_relation
WHERE type in ('DEBITOR') -- TODO.rbac: currently manually patched, needs to be generated
WHERE type = 'DEBITOR'
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_booking_item'),
@ -128,11 +128,11 @@ create or replace function hs_booking_item_hs_office_relation_insert_tf()
language plpgsql
strict as $$
begin
if NEW.type = 'DEBITOR' then -- TODO.rbac: currently manually patched, needs to be generated
call grantPermissionToRole(
if NEW.type = 'DEBITOR' then
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_booking_item'),
hsOfficeRelationADMIN(NEW));
end if;
end if;
return NEW;
end; $$;

View File

@ -141,8 +141,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(from(
initialGrantNames,
// TODO.rbac: this grant should only be created for DEBITOR-Relationships, thus the RBAC DSL needs to support conditional grants
"{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:INSERT>sepamandate to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
// permissions on partner
"{ grant perm:partner#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",

View File

@ -131,8 +131,6 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// TODO.rbac: this grant should only be created for DEBITOR-Relationships, thus the RBAC DSL needs to support conditional grants
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:INSERT>hs_office_sepamandate to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:DELETE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to role:global#global:ADMIN by system and assume }",