Compare commits
No commits in common. "4f25bf14967f1a6027a6d0a982c49901e0388458" and "952fafffdb28426937f4168c34c15a51c56bd1bc" have entirely different histories.
4f25bf1496
...
952fafffdb
@ -104,18 +104,21 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type = 'REPRESENTATIVE'");
|
// TODO: if type=REPRESENTATIIVE
|
||||||
|
// with.incomingSuperRole("holderPerson", ADMIN);
|
||||||
with.permission(DELETE);
|
with.permission(DELETE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.incomingSuperRole("anchorPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'");
|
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||||
with.outgoingSubRole("anchorPerson", OWNER).where("${REF}.type = 'REPRESENTATIVE'");
|
// TODO: if type=REPRESENTATIIVE
|
||||||
|
// with.outgoingSuperRole("anchorPerson", OWNER);
|
||||||
with.permission(UPDATE);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(AGENT, (with) -> {
|
.createSubRole(AGENT, (with) -> {
|
||||||
with.incomingSuperRole("holderPerson", ADMIN).where("${REF}.type <> 'REPRESENTATIVE'");
|
with.incomingSuperRole("holderPerson", ADMIN);
|
||||||
})
|
})
|
||||||
.createSubRole(TENANT, (with) -> {
|
.createSubRole(TENANT, (with) -> {
|
||||||
|
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);
|
||||||
|
@ -496,13 +496,10 @@ 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;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final var arrow = isConditional()
|
final var arrow = isAssumed() ? " --> " : " -- // --> ";
|
||||||
? (isAssumed() ? " -- ?? --> " : " -- ?//? --> ")
|
|
||||||
: (isAssumed() ? " --> " : " -- // --> ");
|
|
||||||
return switch (grantType()) {
|
return switch (grantType()) {
|
||||||
case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString();
|
case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString();
|
||||||
case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef;
|
case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef;
|
||||||
@ -549,10 +546,6 @@ public class RbacView {
|
|||||||
return assumed;
|
return assumed;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isConditional() {
|
|
||||||
return sqlWhere != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isToCreate() {
|
boolean isToCreate() {
|
||||||
return toCreate;
|
return toCreate;
|
||||||
}
|
}
|
||||||
@ -574,14 +567,8 @@ public class RbacView {
|
|||||||
.orElse(false);
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RbacGrantDefinition unassumed() {
|
public void unassumed() {
|
||||||
this.assumed = false;
|
this.assumed = false;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RbacGrantDefinition where(final String sqlWhere) {
|
|
||||||
this.sqlWhere = sqlWhere;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GrantType {
|
public enum GrantType {
|
||||||
|
@ -109,9 +109,7 @@ public class RbacViewMermaidFlowchartGenerator {
|
|||||||
|
|
||||||
private String grantDef(final RbacView.RbacGrantDefinition grant) {
|
private String grantDef(final RbacView.RbacGrantDefinition grant) {
|
||||||
final var arrow = (grant.isToCreate() ? " ==>" : " -.->")
|
final var arrow = (grant.isToCreate() ? " ==>" : " -.->")
|
||||||
+ (grant.isConditional()
|
+ (grant.isAssumed() ? " " : "|XX| ");
|
||||||
? (grant.isAssumed() ? " |??| " : "|?XX?| ")
|
|
||||||
: (grant.isAssumed() ? " " : "|XX| "));
|
|
||||||
return switch (grant.grantType()) {
|
return switch (grant.grantType()) {
|
||||||
case ROLE_TO_USER ->
|
case ROLE_TO_USER ->
|
||||||
// TODO: other user types not implemented yet
|
// TODO: other user types not implemented yet
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition;
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -23,7 +22,7 @@ import static org.apache.commons.lang3.StringUtils.uncapitalize;
|
|||||||
class RolesGrantsAndPermissionsGenerator {
|
class RolesGrantsAndPermissionsGenerator {
|
||||||
|
|
||||||
private final RbacView rbacDef;
|
private final RbacView rbacDef;
|
||||||
private final Set<RbacGrantDefinition> rbacGrants = new HashSet<>();
|
private final Set<RbacView.RbacGrantDefinition> rbacGrants = new HashSet<>();
|
||||||
private final String liquibaseTagPrefix;
|
private final String liquibaseTagPrefix;
|
||||||
private final String simpleEntityName;
|
private final String simpleEntityName;
|
||||||
private final String simpleEntityVarName;
|
private final String simpleEntityVarName;
|
||||||
@ -32,7 +31,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
|
RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
|
||||||
this.rbacDef = rbacDef;
|
this.rbacDef = rbacDef;
|
||||||
this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
|
this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
|
||||||
.filter(RbacGrantDefinition::isToCreate)
|
.filter(RbacView.RbacGrantDefinition::isToCreate)
|
||||||
.collect(toSet()));
|
.collect(toSet()));
|
||||||
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
this.liquibaseTagPrefix = liquibaseTagPrefix;
|
||||||
|
|
||||||
@ -68,11 +67,13 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
NEW ${rawTableName}
|
NEW ${rawTableName}
|
||||||
)
|
)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
|
|
||||||
|
declare
|
||||||
"""
|
"""
|
||||||
.replace("${simpleEntityName}", simpleEntityName)
|
.replace("${simpleEntityName}", simpleEntityName)
|
||||||
.replace("${rawTableName}", rawTableName));
|
.replace("${rawTableName}", rawTableName));
|
||||||
|
|
||||||
plPgSql.writeLn("declare");
|
plPgSql.chopEmptyLines();
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
referencedEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
|
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
|
||||||
@ -171,10 +172,6 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.anyMatch(e -> true);
|
.anyMatch(e -> true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasAnyConditionalGrants() {
|
|
||||||
return rbacDef.getGrantDefs().stream().anyMatch(RbacGrantDefinition::isConditional);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
|
||||||
referencedEntityAliases()
|
referencedEntityAliases()
|
||||||
.forEach((ea) -> {
|
.forEach((ea) -> {
|
||||||
@ -251,7 +248,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
|
|
||||||
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
|
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
|
||||||
rbacDef.getGrantDefs().stream()
|
rbacDef.getGrantDefs().stream()
|
||||||
.filter(RbacGrantDefinition::isToCreate)
|
.filter(RbacView.RbacGrantDefinition::isToCreate)
|
||||||
.filter(g -> g.dependsOnColumn(columnName))
|
.filter(g -> g.dependsOnColumn(columnName))
|
||||||
.filter(g -> !isInsertPermissionGrant(g))
|
.filter(g -> !isInsertPermissionGrant(g))
|
||||||
.forEach(g -> {
|
.forEach(g -> {
|
||||||
@ -262,21 +259,21 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Boolean isInsertPermissionGrant(final RbacGrantDefinition g) {
|
private static Boolean isInsertPermissionGrant(final RbacView.RbacGrantDefinition g) {
|
||||||
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
|
final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
|
||||||
return isInsertPermissionGrant;
|
return isInsertPermissionGrant;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateGrants(final StringWriter plPgSql, final RbacGrantDefinition.GrantType grantType) {
|
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
rbacGrants.stream()
|
rbacGrants.stream()
|
||||||
.filter(g -> g.grantType() == grantType)
|
.filter(g -> g.grantType() == grantType)
|
||||||
.map(this::generateGrant)
|
.map(this::generateGrant)
|
||||||
.sorted()
|
.sorted()
|
||||||
.forEach(text -> plPgSql.writeLn(text, with("ref", NEW.name())));
|
.forEach(text -> plPgSql.writeLn(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateRevoke(RbacGrantDefinition grantDef) {
|
private String generateRevoke(RbacView.RbacGrantDefinition grantDef) {
|
||||||
return switch (grantDef.grantType()) {
|
return switch (grantDef.grantType()) {
|
||||||
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
|
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
|
||||||
case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
|
case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
|
||||||
@ -288,8 +285,8 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateGrant(RbacGrantDefinition grantDef) {
|
private String generateGrant(RbacView.RbacGrantDefinition grantDef) {
|
||||||
final var grantSql = switch (grantDef.grantType()) {
|
return switch (grantDef.grantType()) {
|
||||||
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
|
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
|
||||||
case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
|
case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
|
||||||
.replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
|
.replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
|
||||||
@ -301,12 +298,6 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
|
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
|
||||||
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
|
.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) {
|
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
@ -389,7 +380,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
|
final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
if (!grantsToUsers.isEmpty()) {
|
if (!grantsToUsers.isEmpty()) {
|
||||||
final var arrayElements = grantsToUsers.stream()
|
final var arrayElements = grantsToUsers.stream()
|
||||||
.map(RbacGrantDefinition::getUserDef)
|
.map(RbacView.RbacGrantDefinition::getUserDef)
|
||||||
.map(this::toPlPgSqlReference)
|
.map(this::toPlPgSqlReference)
|
||||||
.toList();
|
.toList();
|
||||||
plPgSql.indented(() ->
|
plPgSql.indented(() ->
|
||||||
@ -402,7 +393,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
|
final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
if (!permissionGrantsForRole.isEmpty()) {
|
if (!permissionGrantsForRole.isEmpty()) {
|
||||||
final var arrayElements = permissionGrantsForRole.stream()
|
final var arrayElements = permissionGrantsForRole.stream()
|
||||||
.map(RbacGrantDefinition::getPermDef)
|
.map(RbacView.RbacGrantDefinition::getPermDef)
|
||||||
.map(RbacPermissionDefinition::getPermission)
|
.map(RbacPermissionDefinition::getPermission)
|
||||||
.map(RbacView.Permission::name)
|
.map(RbacView.Permission::name)
|
||||||
.map(p -> "'" + p + "'")
|
.map(p -> "'" + p + "'")
|
||||||
@ -415,30 +406,26 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
final var unconditionalIncomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
|
final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
.filter(g -> !g.isConditional())
|
if (!incomingGrants.isEmpty()) {
|
||||||
.toList();
|
final var arrayElements = incomingGrants.stream()
|
||||||
if (!unconditionalIncomingGrants.isEmpty()) {
|
|
||||||
final var arrayElements = unconditionalIncomingGrants.stream()
|
|
||||||
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
|
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
|
||||||
.sorted().toList();
|
.sorted().toList();
|
||||||
plPgSql.indented(() ->
|
plPgSql.indented(() ->
|
||||||
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||||
rbacGrants.removeAll(unconditionalIncomingGrants);
|
rbacGrants.removeAll(incomingGrants);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
|
||||||
final var unconditionalOutgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role).stream()
|
final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
|
||||||
.filter(g -> !g.isConditional())
|
if (!outgoingGrants.isEmpty()) {
|
||||||
.toList();
|
final var arrayElements = outgoingGrants.stream()
|
||||||
if (!unconditionalOutgoingGrants.isEmpty()) {
|
|
||||||
final var arrayElements = unconditionalOutgoingGrants.stream()
|
|
||||||
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
|
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
|
||||||
.sorted().toList();
|
.sorted().toList();
|
||||||
plPgSql.indented(() ->
|
plPgSql.indented(() ->
|
||||||
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
|
||||||
rbacGrants.removeAll(unconditionalOutgoingGrants);
|
rbacGrants.removeAll(outgoingGrants);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,7 +435,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
|
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<RbacGrantDefinition> findPermissionsGrantsForRole(
|
private Set<RbacView.RbacGrantDefinition> findPermissionsGrantsForRole(
|
||||||
final RbacView.EntityAlias entityAlias,
|
final RbacView.EntityAlias entityAlias,
|
||||||
final RbacView.Role role) {
|
final RbacView.Role role) {
|
||||||
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
||||||
@ -457,7 +444,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<RbacGrantDefinition> findGrantsToUserForRole(
|
private Set<RbacView.RbacGrantDefinition> findGrantsToUserForRole(
|
||||||
final RbacView.EntityAlias entityAlias,
|
final RbacView.EntityAlias entityAlias,
|
||||||
final RbacView.Role role) {
|
final RbacView.Role role) {
|
||||||
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
||||||
@ -466,7 +453,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<RbacGrantDefinition> findIncomingSuperRolesForRole(
|
private Set<RbacView.RbacGrantDefinition> findIncomingSuperRolesForRole(
|
||||||
final RbacView.EntityAlias entityAlias,
|
final RbacView.EntityAlias entityAlias,
|
||||||
final RbacView.Role role) {
|
final RbacView.Role role) {
|
||||||
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
||||||
@ -475,7 +462,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<RbacGrantDefinition> findOutgoingSuperRolesForRole(
|
private Set<RbacView.RbacGrantDefinition> findOutgoingSuperRolesForRole(
|
||||||
final RbacView.EntityAlias entityAlias,
|
final RbacView.EntityAlias entityAlias,
|
||||||
final RbacView.Role role) {
|
final RbacView.Role role) {
|
||||||
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
final var roleDef = rbacDef.findRbacRole(entityAlias, role);
|
||||||
@ -519,7 +506,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
private void generateUpdateTrigger(final StringWriter plPgSql) {
|
||||||
|
|
||||||
generateHeader(plPgSql, "update");
|
generateHeader(plPgSql, "update");
|
||||||
if ( hasAnyUpdatableAndNullableEntityAliases() || hasAnyConditionalGrants() ) {
|
if ( hasAnyUpdatableAndNullableEntityAliases() ) {
|
||||||
generateSimplifiedUpdateTriggerFunction(plPgSql);
|
generateSimplifiedUpdateTriggerFunction(plPgSql);
|
||||||
} else {
|
} else {
|
||||||
generateUpdateTriggerFunction(plPgSql);
|
generateUpdateTriggerFunction(plPgSql);
|
||||||
|
@ -82,13 +82,12 @@ role:global:ADMIN -.-> role:contact:OWNER
|
|||||||
role:contact:OWNER -.-> role:contact:ADMIN
|
role:contact:OWNER -.-> role:contact:ADMIN
|
||||||
role:contact:ADMIN -.-> role:contact:REFERRER
|
role:contact:ADMIN -.-> role:contact:REFERRER
|
||||||
role:global:ADMIN ==> role:relation:OWNER
|
role:global:ADMIN ==> role:relation:OWNER
|
||||||
role:holderPerson:ADMIN ==> |??| role:relation:OWNER
|
|
||||||
role:relation:OWNER ==> role:relation:ADMIN
|
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:relation:ADMIN ==> role:relation:AGENT
|
||||||
role:holderPerson:ADMIN ==> |??| role:relation:AGENT
|
role:holderPerson:ADMIN ==> role:relation:AGENT
|
||||||
role:relation:AGENT ==> role:relation:TENANT
|
role:relation:AGENT ==> role:relation:TENANT
|
||||||
|
role:holderPerson:ADMIN ==> role:relation:TENANT
|
||||||
role:contact:ADMIN ==> role:relation:TENANT
|
role:contact:ADMIN ==> role:relation:TENANT
|
||||||
role:relation:TENANT ==> role:anchorPerson:REFERRER
|
role:relation:TENANT ==> role:anchorPerson:REFERRER
|
||||||
role:relation:TENANT ==> role:holderPerson:REFERRER
|
role:relation:TENANT ==> role:holderPerson:REFERRER
|
||||||
|
@ -57,12 +57,16 @@ begin
|
|||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationADMIN(NEW),
|
hsOfficeRelationADMIN(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeRelationOWNER(NEW)]
|
incomingSuperRoles => array[
|
||||||
|
hsOfficePersonADMIN(newAnchorPerson),
|
||||||
|
hsOfficeRelationOWNER(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationAGENT(NEW),
|
hsOfficeRelationAGENT(NEW),
|
||||||
incomingSuperRoles => array[hsOfficeRelationADMIN(NEW)]
|
incomingSuperRoles => array[
|
||||||
|
hsOfficePersonADMIN(newHolderPerson),
|
||||||
|
hsOfficeRelationADMIN(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
@ -70,6 +74,7 @@ begin
|
|||||||
permissions => array['SELECT'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeContactADMIN(newContact),
|
hsOfficeContactADMIN(newContact),
|
||||||
|
hsOfficePersonADMIN(newHolderPerson),
|
||||||
hsOfficeRelationAGENT(NEW)],
|
hsOfficeRelationAGENT(NEW)],
|
||||||
outgoingSubRoles => array[
|
outgoingSubRoles => array[
|
||||||
hsOfficeContactREFERRER(newContact),
|
hsOfficeContactREFERRER(newContact),
|
||||||
@ -77,19 +82,6 @@ begin
|
|||||||
hsOfficePersonREFERRER(newHolderPerson)]
|
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);
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
@ -126,12 +118,48 @@ create or replace procedure updateRbacRulesForHsOfficeRelation(
|
|||||||
NEW hs_office_relation
|
NEW hs_office_relation
|
||||||
)
|
)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
|
||||||
|
|
||||||
if NEW.contactUuid is distinct from OLD.contactUuid then
|
declare
|
||||||
delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
|
oldHolderPerson hs_office_person;
|
||||||
call buildRbacSystemForHsOfficeRelation(NEW);
|
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));
|
||||||
|
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -103,69 +103,6 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
|
|||||||
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
|
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
|
||||||
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.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
|
// when
|
||||||
attempt(em, () -> {
|
attempt(em, () -> {
|
||||||
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
|
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
|
||||||
@ -203,9 +140,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 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_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER 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:ADMIN to role:hs_office_person#ErbenBesslerMelBessler: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_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 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 }",
|
"{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
|
||||||
@ -216,6 +153,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
|
|||||||
|
|
||||||
// REPRESENTATIVE holder person -> (represented) anchor person
|
// 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_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)
|
null)
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user