Compare commits

..

2 Commits

Author SHA1 Message Date
Michael Hoennig
4f25bf1496 WIP: rbac-generator-with-conditional-grants 2024-04-02 10:09:28 +02:00
Michael Hoennig
6346dff3ef remove superfluous grant to TENANT which was also granted to AGENT 2024-04-02 09:32:16 +02:00
7 changed files with 149 additions and 89 deletions

View File

@ -104,21 +104,18 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
// TODO: if type=REPRESENTATIIVE
// with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type = 'REPRESENTATIVE'");
with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("anchorPerson", ADMIN);
// TODO: if type=REPRESENTATIIVE
// with.outgoingSuperRole("anchorPerson", OWNER);
with.incomingSuperRole("anchorPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'");
with.outgoingSubRole("anchorPerson", OWNER).where("${REF}.type = 'REPRESENTATIVE'");
with.permission(UPDATE);
})
.createSubRole(AGENT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'");
})
.createSubRole(TENANT, (with) -> {
with.incomingSuperRole("holderPerson", ADMIN);
with.incomingSuperRole("contact", ADMIN);
with.outgoingSubRole("anchorPerson", REFERRER);
with.outgoingSubRole("holderPerson", REFERRER);

View File

@ -496,10 +496,13 @@ public class RbacView {
private final RbacPermissionDefinition permDef;
private boolean assumed = true;
private boolean toCreate = false;
private String sqlWhere;
@Override
public String toString() {
final var arrow = isAssumed() ? " --> " : " -- // --> ";
final var arrow = isConditional()
? (isAssumed() ? " -- ?? --> " : " -- ?//? --> ")
: (isAssumed() ? " --> " : " -- // --> ");
return switch (grantType()) {
case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString();
case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef;
@ -546,6 +549,10 @@ public class RbacView {
return assumed;
}
boolean isConditional() {
return sqlWhere != null;
}
boolean isToCreate() {
return toCreate;
}
@ -567,8 +574,14 @@ public class RbacView {
.orElse(false);
}
public void unassumed() {
public RbacGrantDefinition unassumed() {
this.assumed = false;
return this;
}
public RbacGrantDefinition where(final String sqlWhere) {
this.sqlWhere = sqlWhere;
return this;
}
public enum GrantType {

View File

@ -109,7 +109,9 @@ public class RbacViewMermaidFlowchartGenerator {
private String grantDef(final RbacView.RbacGrantDefinition grant) {
final var arrow = (grant.isToCreate() ? " ==>" : " -.->")
+ (grant.isAssumed() ? " " : "|XX| ");
+ (grant.isConditional()
? (grant.isAssumed() ? " |??| " : "|?XX?| ")
: (grant.isAssumed() ? " " : "|XX| "));
return switch (grant.grantType()) {
case ROLE_TO_USER ->
// TODO: other user types not implemented yet

View File

@ -1,5 +1,6 @@
package net.hostsharing.hsadminng.rbac.rbacdef;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
import java.util.HashSet;
@ -22,7 +23,7 @@ import static org.apache.commons.lang3.StringUtils.uncapitalize;
class RolesGrantsAndPermissionsGenerator {
private final RbacView rbacDef;
private final Set<RbacView.RbacGrantDefinition> rbacGrants = new HashSet<>();
private final Set<RbacGrantDefinition> rbacGrants = new HashSet<>();
private final String liquibaseTagPrefix;
private final String simpleEntityName;
private final String simpleEntityVarName;
@ -31,7 +32,7 @@ class RolesGrantsAndPermissionsGenerator {
RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef;
this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate)
.filter(RbacGrantDefinition::isToCreate)
.collect(toSet()));
this.liquibaseTagPrefix = liquibaseTagPrefix;
@ -67,13 +68,11 @@ class RolesGrantsAndPermissionsGenerator {
NEW ${rawTableName}
)
language plpgsql as $$
declare
"""
.replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableName}", rawTableName));
plPgSql.chopEmptyLines();
plPgSql.writeLn("declare");
plPgSql.indented(() -> {
referencedEntityAliases()
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
@ -172,6 +171,10 @@ class RolesGrantsAndPermissionsGenerator {
.anyMatch(e -> true);
}
private boolean hasAnyConditionalGrants() {
return rbacDef.getGrantDefs().stream().anyMatch(RbacGrantDefinition::isConditional);
}
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
referencedEntityAliases()
.forEach((ea) -> {
@ -248,7 +251,7 @@ class RolesGrantsAndPermissionsGenerator {
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate)
.filter(RbacGrantDefinition::isToCreate)
.filter(g -> g.dependsOnColumn(columnName))
.filter(g -> !isInsertPermissionGrant(g))
.forEach(g -> {
@ -259,21 +262,21 @@ class RolesGrantsAndPermissionsGenerator {
});
}
private static Boolean isInsertPermissionGrant(final RbacView.RbacGrantDefinition g) {
private static Boolean isInsertPermissionGrant(final RbacGrantDefinition g) {
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
return isInsertPermissionGrant;
}
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType) {
plPgSql.ensureSingleEmptyLine();
rbacGrants.stream()
.filter(g -> g.grantType() == grantType)
.map(this::generateGrant)
.sorted()
.forEach(text -> plPgSql.writeLn(text));
.forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
}
private String generateRevoke(RbacView.RbacGrantDefinition grantDef) {
private String generateRevoke(RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
@ -285,8 +288,8 @@ class RolesGrantsAndPermissionsGenerator {
};
}
private String generateGrant(RbacView.RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) {
private String generateGrant(RbacGrantDefinition grantDef) {
final var grantSql = switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
.replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
@ -298,6 +301,12 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
};
if (grantDef.isConditional()) {
return "if " + grantDef.getSqlWhere() + " then\n"
+ " " + grantSql + "\n"
+ "end if;";
}
return grantSql;
}
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
@ -380,7 +389,7 @@ class RolesGrantsAndPermissionsGenerator {
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
if (!grantsToUsers.isEmpty()) {
final var arrayElements = grantsToUsers.stream()
.map(RbacView.RbacGrantDefinition::getUserDef)
.map(RbacGrantDefinition::getUserDef)
.map(this::toPlPgSqlReference)
.toList();
plPgSql.indented(() ->
@ -393,7 +402,7 @@ class RolesGrantsAndPermissionsGenerator {
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
if (!permissionGrantsForRole.isEmpty()) {
final var arrayElements = permissionGrantsForRole.stream()
.map(RbacView.RbacGrantDefinition::getPermDef)
.map(RbacGrantDefinition::getPermDef)
.map(RbacPermissionDefinition::getPermission)
.map(RbacView.Permission::name)
.map(p -> "'" + p + "'")
@ -406,26 +415,30 @@ class RolesGrantsAndPermissionsGenerator {
}
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
if (!incomingGrants.isEmpty()) {
final var arrayElements = incomingGrants.stream()
final var unconditionalIncomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
.filter(g -> !g.isConditional())
.toList();
if (!unconditionalIncomingGrants.isEmpty()) {
final var arrayElements = unconditionalIncomingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
.sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(incomingGrants);
rbacGrants.removeAll(unconditionalIncomingGrants);
}
}
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
if (!outgoingGrants.isEmpty()) {
final var arrayElements = outgoingGrants.stream()
final var unconditionalOutgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
.filter(g -> !g.isConditional())
.toList();
if (!unconditionalOutgoingGrants.isEmpty()) {
final var arrayElements = unconditionalOutgoingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
.sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(outgoingGrants);
rbacGrants.removeAll(unconditionalOutgoingGrants);
}
}
@ -435,7 +448,7 @@ class RolesGrantsAndPermissionsGenerator {
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
}
private Set<RbacView.RbacGrantDefinition> findPermissionsGrantsForRole(
private Set<RbacGrantDefinition> findPermissionsGrantsForRole(
final RbacView.EntityAlias entityAlias,
final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -444,7 +457,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet());
}
private Set<RbacView.RbacGrantDefinition> findGrantsToUserForRole(
private Set<RbacGrantDefinition> findGrantsToUserForRole(
final RbacView.EntityAlias entityAlias,
final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -453,7 +466,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet());
}
private Set<RbacView.RbacGrantDefinition> findIncomingSuperRolesForRole(
private Set<RbacGrantDefinition> findIncomingSuperRolesForRole(
final RbacView.EntityAlias entityAlias,
final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -462,7 +475,7 @@ class RolesGrantsAndPermissionsGenerator {
.collect(toSet());
}
private Set<RbacView.RbacGrantDefinition> findOutgoingSuperRolesForRole(
private Set<RbacGrantDefinition> findOutgoingSuperRolesForRole(
final RbacView.EntityAlias entityAlias,
final RbacView.Role role) {
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
@ -506,7 +519,7 @@ class RolesGrantsAndPermissionsGenerator {
private void generateUpdateTrigger(final StringWriter plPgSql) {
generateHeader(plPgSql, "update");
if ( hasAnyUpdatableAndNullableEntityAliases() ) {
if ( hasAnyUpdatableAndNullableEntityAliases() || hasAnyConditionalGrants() ) {
generateSimplifiedUpdateTriggerFunction(plPgSql);
} else {
generateUpdateTriggerFunction(plPgSql);

View File

@ -82,12 +82,13 @@ role:global:ADMIN -.-> role:contact:OWNER
role:contact:OWNER -.-> role:contact:ADMIN
role:contact:ADMIN -.-> role:contact:REFERRER
role:global:ADMIN ==> role:relation:OWNER
role:holderPerson:ADMIN ==> |??| role:relation:OWNER
role:relation:OWNER ==> role:relation:ADMIN
role:anchorPerson:ADMIN ==> role:relation:ADMIN
role:anchorPerson:ADMIN ==> |??| role:relation:ADMIN
role:relation:ADMIN ==> |??| role:anchorPerson:OWNER
role:relation:ADMIN ==> role:relation:AGENT
role:holderPerson:ADMIN ==> role:relation:AGENT
role:holderPerson:ADMIN ==> |??| role:relation:AGENT
role:relation:AGENT ==> role:relation:TENANT
role:holderPerson:ADMIN ==> role:relation:TENANT
role:contact:ADMIN ==> role:relation:TENANT
role:relation:TENANT ==> role:anchorPerson:REFERRER
role:relation:TENANT ==> role:holderPerson:REFERRER

View File

@ -57,16 +57,12 @@ begin
perform createRoleWithGrants(
hsOfficeRelationADMIN(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[
hsOfficePersonADMIN(newAnchorPerson),
hsOfficeRelationOWNER(NEW)]
incomingSuperRoles => array[hsOfficeRelationOWNER(NEW)]
);
perform createRoleWithGrants(
hsOfficeRelationAGENT(NEW),
incomingSuperRoles => array[
hsOfficePersonADMIN(newHolderPerson),
hsOfficeRelationADMIN(NEW)]
incomingSuperRoles => array[hsOfficeRelationADMIN(NEW)]
);
perform createRoleWithGrants(
@ -74,7 +70,6 @@ begin
permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeContactADMIN(newContact),
hsOfficePersonADMIN(newHolderPerson),
hsOfficeRelationAGENT(NEW)],
outgoingSubRoles => array[
hsOfficeContactREFERRER(newContact),
@ -82,6 +77,19 @@ begin
hsOfficePersonREFERRER(newHolderPerson)]
);
if NEW.type <> 'REPRESENTATIVE' then
call grantRoleToRole(hsOfficeRelationADMIN(NEW), hsOfficePersonADMIN(newAnchorPerson));
end if;
if NEW.type <> 'REPRESENTATIVE' then
call grantRoleToRole(hsOfficeRelationAGENT(NEW), hsOfficePersonADMIN(newHolderPerson));
end if;
if NEW.type = 'REPRESENTATIVE' then
call grantRoleToRole(hsOfficePersonOWNER(newAnchorPerson), hsOfficeRelationADMIN(NEW));
end if;
if NEW.type = 'REPRESENTATIVE' then
call grantRoleToRole(hsOfficeRelationOWNER(NEW), hsOfficePersonADMIN(newHolderPerson));
end if;
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
@ -118,48 +126,12 @@ create or replace procedure updateRbacRulesForHsOfficeRelation(
NEW hs_office_relation
)
language plpgsql as $$
declare
oldHolderPerson hs_office_person;
newHolderPerson hs_office_person;
oldAnchorPerson hs_office_person;
newAnchorPerson hs_office_person;
oldContact hs_office_contact;
newContact hs_office_contact;
begin
call enterTriggerForObjectUuid(NEW.uuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.holderUuid INTO oldHolderPerson;
assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
SELECT * FROM hs_office_person WHERE uuid = OLD.anchorUuid INTO oldAnchorPerson;
assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid);
SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
SELECT * FROM hs_office_contact WHERE uuid = OLD.contactUuid INTO oldContact;
assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid);
SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
if NEW.contactUuid <> OLD.contactUuid then
call revokeRoleFromRole(hsOfficeRelationTENANT(OLD), hsOfficeContactADMIN(oldContact));
call grantRoleToRole(hsOfficeRelationTENANT(NEW), hsOfficeContactADMIN(newContact));
call revokeRoleFromRole(hsOfficeContactREFERRER(oldContact), hsOfficeRelationTENANT(OLD));
call grantRoleToRole(hsOfficeContactREFERRER(newContact), hsOfficeRelationTENANT(NEW));
if NEW.contactUuid is distinct from OLD.contactUuid then
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
call buildRbacSystemForHsOfficeRelation(NEW);
end if;
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
/*

View File

@ -103,6 +103,69 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
// when
attempt(em, () -> {
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
.filter(p -> p.getPersonType() == UNINCORPORATED_FIRM)
.findFirst().orElseThrow();
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Bert").stream()
.filter(p -> p.getPersonType() == NATURAL_PERSON)
.findFirst().orElseThrow();
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").stream()
.findFirst().orElseThrow();
final var newRelation = HsOfficeRelationEntity.builder()
.anchor(givenAnchorPerson)
.holder(givenHolderPerson)
.type(HsOfficeRelationType.SUBSCRIBER)
.mark("dummy")
.contact(givenContact)
.build();
return toCleanup(relationRepo.save(newRelation));
});
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT",
"hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// TODO: 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-SUBSCRIBER-BesslerBert:INSERT>hs_office_sepamandate to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:DELETE to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER to role:global#global:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER to user:superuser-alex@hostsharing.net by hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:AGENT by system and assume }",
"{ grant role:hs_office_person#BesslerBert:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_person#ErbenBesslerMelBessler:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
"{ grant role:hs_office_contact#fourthcontact:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT by system and assume }",
// SUBSCRIBER holder person -> (represented) anchor person
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-SUBSCRIBER-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
null)
);
}
@Test
public void createsAndGrantsRolesForTypeRepresentative() {
// given
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
// when
attempt(em, () -> {
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
@ -140,9 +203,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
"{ grant role:hs_office_person#ErbenBesslerMelBessler:OWNER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
@ -153,7 +216,6 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// REPRESENTATIVE holder person -> (represented) anchor person
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
"{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
null)
);