WIP
This commit is contained in:
parent
e422db9081
commit
9788205724
@ -59,8 +59,11 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
||||
.withIdentityView(SQL.projection("iban || ':' || holder"))
|
||||
.withIdentityView(SQL.projection("concat(iban, ':', holder)"))
|
||||
.withUpdatableColumns("holder", "iban", "bic")
|
||||
|
||||
.toRole("global", GUEST).grantPermission("bankAccount", INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||
@ -75,6 +78,6 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
|
||||
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
||||
return rbacViewFor("person", HsOfficePersonEntity.class)
|
||||
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
|
||||
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
||||
.toRole("global", GUEST).grantPermission("person", INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.permission(DELETE);
|
||||
with.owningUser(CREATOR);
|
||||
@ -93,6 +95,6 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
|
||||
rbac().generateWithBaseFileName("213-hs-office-person-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
|
||||
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
|
||||
.withIdentityView(query("""
|
||||
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
|
||||
from hs_office_sepamandate sm
|
||||
join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(expression("validity"))
|
||||
.withUpdatableColumns("reference", "agreement", "validity")
|
||||
|
||||
.importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid"))
|
||||
@ -111,17 +116,17 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
||||
})
|
||||
.createSubRole(AGENT, (with) -> {
|
||||
with.outgoingSubRole("bankAccount", REFERRER);
|
||||
with.outgoingSubRole("debitorRel", AGENT);
|
||||
with.outgoingSubRole("debitor", AGENT);
|
||||
})
|
||||
.createSubRole(REFERRER, (with) -> {
|
||||
with.incomingSuperRole("bankAccount", ADMIN);
|
||||
with.incomingSuperRole("debitorRel", AGENT);
|
||||
with.incomingSuperRole("debitor", AGENT);
|
||||
with.outgoingSubRole("debitorRel", TENANT);
|
||||
with.permission(SELECT);
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
|
||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public class InsertTriggerGenerator {
|
||||
|
||||
FOR row IN SELECT * FROM ${rawSuperTableName}
|
||||
LOOP
|
||||
roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
|
||||
roleUuid := findRoleId(${rawSuperRoleDescriptor});
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||
END LOOP;
|
||||
@ -62,7 +62,7 @@ public class InsertTriggerGenerator {
|
||||
""",
|
||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -79,7 +79,7 @@ public class InsertTriggerGenerator {
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
${rawSuperRoleDescriptor}(NEW),
|
||||
${rawSuperRoleDescriptor},
|
||||
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
|
||||
return NEW;
|
||||
end; $$;
|
||||
@ -91,7 +91,7 @@ public class InsertTriggerGenerator {
|
||||
""",
|
||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, PostgresTriggerReference.NEW.name()))
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -111,15 +111,39 @@ public class InsertTriggerGenerator {
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||
getOptionalInsertGrant().ifPresentOrElse(g -> {
|
||||
plPgSql.writeLn("""
|
||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||
before insert on ${rawSubTable}
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
|
||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
|
||||
if (!g.getSuperRoleDef().getEntityAlias().isGlobal()) {
|
||||
plPgSql.writeLn(
|
||||
"""
|
||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||
before insert on ${rawSubTable}
|
||||
for each row
|
||||
when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
|
||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
|
||||
} else {
|
||||
switch (g.getSuperRoleDef().getRole()) {
|
||||
case ADMIN -> {
|
||||
plPgSql.writeLn(
|
||||
"""
|
||||
create trigger ${rawSubTable}_insert_permission_check_tg
|
||||
before insert on ${rawSubTable}
|
||||
for each row
|
||||
when ( not isGlobalAdmin() )
|
||||
execute procedure ${rawSubTable}_insert_permission_missing_tf();
|
||||
""",
|
||||
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
|
||||
}
|
||||
case GUEST -> {
|
||||
// no permission check trigger generated, as anybody can insert rows into this table
|
||||
}
|
||||
default -> {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
plPgSql.writeLn("""
|
||||
@ -162,4 +186,12 @@ public class InsertTriggerGenerator {
|
||||
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
|
||||
}
|
||||
|
||||
|
||||
private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
|
||||
final var functionName = toVar(roleDef);
|
||||
if (roleDef.getEntityAlias().isGlobal()) {
|
||||
return functionName + "()";
|
||||
}
|
||||
return functionName + "(" + ref + ")";
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class RbacIdentityViewGenerator {
|
||||
$idName$);
|
||||
""";
|
||||
case SQL_QUERY -> """
|
||||
call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
|
||||
call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$
|
||||
${identityViewSqlPart}
|
||||
$idName$);
|
||||
""";
|
||||
|
@ -24,9 +24,11 @@ public class RbacRestrictedViewGenerator {
|
||||
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('${rawTableName}',
|
||||
'${orderBy}',
|
||||
$orderBy$
|
||||
${orderBy}
|
||||
$orderBy$,
|
||||
$updates$
|
||||
${updates}
|
||||
${updates}
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
|
@ -626,22 +626,23 @@ public class RbacView {
|
||||
return tableName.substring(0, tableName.length() - "_rv".length());
|
||||
}
|
||||
|
||||
public record Role(String roleName) {
|
||||
public enum Role {
|
||||
|
||||
public static final Role OWNER = new Role("owner");
|
||||
public static final Role ADMIN = new Role("admin");
|
||||
public static final Role AGENT = new Role("agent");
|
||||
public static final Role TENANT = new Role("tenant");
|
||||
public static final Role REFERRER = new Role("referrer");
|
||||
OWNER,
|
||||
ADMIN,
|
||||
AGENT,
|
||||
TENANT,
|
||||
REFERRER,
|
||||
|
||||
GUEST;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ":" + roleName;
|
||||
return ":" + roleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
|
||||
String roleName() {
|
||||
return name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
// public ResponseEntity<String> allGrantsOfUserAsMermaid(
|
||||
// @RequestHeader(name = "current-user") String currentUser,
|
||||
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
|
||||
// final var graph = RbacGrantsMermaidService.allGrantsToUser(currentUser);
|
||||
// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
|
||||
// return ResponseEntity.ok(graph);
|
||||
// }
|
||||
|
||||
|
@ -1,204 +0,0 @@
|
||||
package net.hostsharing.hsadminng.rbac.rbacgrant;
|
||||
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*;
|
||||
|
||||
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
|
||||
@Service
|
||||
public class RbacGrantsMermaidService {
|
||||
|
||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
|
||||
writer.write("""
|
||||
### all grants to %s
|
||||
|
||||
```mermaid
|
||||
%s
|
||||
```
|
||||
""".formatted(title, graph));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Include {
|
||||
DETAILS,
|
||||
USERS,
|
||||
PERMISSIONS,
|
||||
NOT_ASSUMED,
|
||||
TEST_ENTITIES,
|
||||
NON_TEST_ENTITIES
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private Context context;
|
||||
|
||||
@Autowired
|
||||
private RawRbacGrantRepository rawGrantRepo;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
|
||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
||||
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
|
||||
traverseGrantsTo(graph, subjectUuid, includes);
|
||||
}
|
||||
return toMermaidFlowchart(graph, includes);
|
||||
}
|
||||
|
||||
private void traverseGrantsTo(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> includes) {
|
||||
final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
|
||||
grants.forEach(g -> {
|
||||
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
|
||||
return;
|
||||
}
|
||||
if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
|
||||
return;
|
||||
}
|
||||
if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
|
||||
return;
|
||||
}
|
||||
graph.add(g);
|
||||
if (includes.contains(NOT_ASSUMED) || g.isAssumed()) {
|
||||
traverseGrantsTo(graph, g.getDescendantUuid(), includes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) {
|
||||
final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbacpermission WHERE objectuuid=:targetObject AND op=:op")
|
||||
.setParameter("targetObject", targetObject)
|
||||
.setParameter("op", op)
|
||||
.getSingleResult();
|
||||
final var graph = new HashSet<RawRbacGrantEntity>();
|
||||
traverseGrantsFrom(graph, refUuid, includes);
|
||||
return toMermaidFlowchart(graph, includes);
|
||||
}
|
||||
|
||||
private void traverseGrantsFrom(final Set<RawRbacGrantEntity> graph, final UUID refUuid, final EnumSet<Include> option) {
|
||||
final var grants = rawGrantRepo.findByDescendantUuid(refUuid);
|
||||
grants.forEach(g -> {
|
||||
if (!option.contains(USERS) && g.getAscendantIdName().startsWith("user ")) {
|
||||
return;
|
||||
}
|
||||
graph.add(g);
|
||||
if (option.contains(NOT_ASSUMED) || g.isAssumed()) {
|
||||
traverseGrantsFrom(graph, g.getAscendingUuid(), option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String toMermaidFlowchart(final HashSet<RawRbacGrantEntity> graph, final EnumSet<Include> includes) {
|
||||
final var entities =
|
||||
includes.contains(DETAILS)
|
||||
? graph.stream()
|
||||
.flatMap(g -> Stream.of(
|
||||
new Node(g.getAscendantIdName(), g.getAscendingUuid()),
|
||||
new Node(g.getDescendantIdName(), g.getDescendantUuid()))
|
||||
)
|
||||
.collect(groupingBy(RbacGrantsMermaidService::renderEntityIdName))
|
||||
.entrySet().stream()
|
||||
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
||||
+ entity.getValue().stream()
|
||||
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
|
||||
.sorted()
|
||||
.distinct()
|
||||
.collect(joining("\n\n ")))
|
||||
.collect(joining("\n\nend\n\n"))
|
||||
+ "\n\nend\n\n"
|
||||
: "";
|
||||
|
||||
final var grants = graph.stream()
|
||||
.map(g -> quoted(g.getAscendantIdName()) +
|
||||
(g.isAssumed() ? " --> " : " -.-> ") +
|
||||
quoted(g.getDescendantIdName()))
|
||||
.sorted()
|
||||
.collect(joining("\n"));
|
||||
|
||||
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
|
||||
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
|
||||
+ "flowchart TB\n\n"
|
||||
+ entities
|
||||
+ grants;
|
||||
}
|
||||
|
||||
private String renderSubgraph(final String entityId) {
|
||||
// this does not work according to Mermaid bug https://github.com/mermaid-js/mermaid/issues/3806
|
||||
// if (entityId.contains("#")) {
|
||||
// final var parts = entityId.split("#");
|
||||
// final var table = parts[0];
|
||||
// final var entity = parts[1];
|
||||
// if (table.equals("entity")) {
|
||||
// return "[" + entity "]";
|
||||
// }
|
||||
// return "[" + table + "\n" + entity + "]";
|
||||
// }
|
||||
return "[" + entityId + "]";
|
||||
}
|
||||
|
||||
private static String renderEntityIdName(final Node node) {
|
||||
final var refType = refType(node.idName());
|
||||
if (refType.equals("user")) {
|
||||
return "users";
|
||||
}
|
||||
if (refType.equals("perm")) {
|
||||
return node.idName().split(" ", 4)[3];
|
||||
}
|
||||
if (refType.equals("role")) {
|
||||
final var withoutRolePrefix = node.idName().substring("role:".length());
|
||||
return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf('.'));
|
||||
}
|
||||
throw new IllegalArgumentException("unknown refType '" + refType + "' in '" + node.idName() + "'");
|
||||
}
|
||||
|
||||
private String renderNode(final String idName, final UUID uuid) {
|
||||
return quoted(idName) + renderNodeContent(idName, uuid);
|
||||
}
|
||||
|
||||
private String renderNodeContent(final String idName, final UUID uuid) {
|
||||
final var refType = refType(idName);
|
||||
|
||||
if (refType.equals("user")) {
|
||||
final var displayName = idName.substring(refType.length()+1);
|
||||
return "(" + displayName + "\nref:" + uuid + ")";
|
||||
}
|
||||
if (refType.equals("role")) {
|
||||
final var roleType = idName.substring(idName.lastIndexOf('.') + 1);
|
||||
return "[" + roleType + "\nref:" + uuid + "]";
|
||||
}
|
||||
if (refType.equals("perm")) {
|
||||
final var roleType = idName.split(" ")[1];
|
||||
return "{{" + roleType + "\nref:" + uuid + "}}";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String refType(final String idName) {
|
||||
return idName.split(" ", 2)[0];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String quoted(final String idName) {
|
||||
return idName.replace(" ", ":").replaceAll("@.*", "");
|
||||
}
|
||||
}
|
||||
|
||||
record Node(String idName, UUID uuid) {
|
||||
|
||||
}
|
@ -41,8 +41,7 @@ public class TestCustomerEntity implements HasUuid {
|
||||
.withIdentityView(SQL.projection("prefix"))
|
||||
.withRestrictedViewOrderBy(SQL.expression("reference"))
|
||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||
// TODO: do we want explicit specification of parent-independent insert permissions?
|
||||
// .toRole("global", ADMIN).grantPermission("customer", INSERT)
|
||||
.toRole("global", ADMIN).grantPermission("customer", INSERT)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR).unassumed();
|
||||
|
@ -373,10 +373,12 @@ create table RbacPermission
|
||||
uuid uuid primary key references RbacReference (uuid) on delete cascade,
|
||||
objectUuid uuid not null references RbacObject,
|
||||
op RbacOp not null,
|
||||
opTableName varchar(60),
|
||||
unique (objectUuid, op)
|
||||
opTableName varchar(60)
|
||||
);
|
||||
|
||||
ALTER TABLE RbacPermission
|
||||
ADD CONSTRAINT RbacPermission_uc UNIQUE NULLS NOT DISTINCT (objectUuid, op, opTableName);
|
||||
|
||||
call create_journal('RbacPermission');
|
||||
|
||||
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
||||
@ -469,23 +471,6 @@ $$;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset rbac-base-duplicate-role-grant-exception:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace procedure raiseDuplicateRoleGrantException(subRoleId uuid, superRoleId uuid)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
subRoleIdName text;
|
||||
superRoleIdName text;
|
||||
begin
|
||||
select roleIdName from rbacRole_ev where uuid=subRoleId into subRoleIdName;
|
||||
select roleIdName from rbacRole_ev where uuid=superRoleId into superRoleIdName;
|
||||
raise exception '[400] Duplicate role grant detected: role % (%) already granted to % (%)', subRoleId, subRoleIdName, superRoleId, superRoleIdName;
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset rbac-base-duplicate-role-grant-exception:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
@ -118,9 +118,32 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'ad
|
||||
$$;
|
||||
|
||||
begin transaction;
|
||||
call defineContext('creating global admin role', null, null, null);
|
||||
select createRole(globalAdmin());
|
||||
call defineContext('creating global admin role', null, null, null);
|
||||
select createRole(globalAdmin());
|
||||
commit;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset rbac-global-GUEST-ROLE:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
A global guest role.
|
||||
*/
|
||||
create or replace function globalGuest(assumed boolean = true)
|
||||
returns RbacRoleDescriptor
|
||||
returns null on null input
|
||||
stable -- leakproof
|
||||
language sql as $$
|
||||
select 'global', (select uuid from RbacObject where objectTable = 'global'), 'guest'::RbacRoleType, assumed;
|
||||
$$;
|
||||
|
||||
begin transaction;
|
||||
call defineContext('creating global guest role', null, null, null);
|
||||
select createRole(globalGuest());
|
||||
commit;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset rbac-global-ADMIN-USERS:1 context:dev,tc endDelimiter:--//
|
||||
|
@ -1,4 +1,5 @@
|
||||
--liquibase formatted sql
|
||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T15:13:04.479330676.
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
||||
@ -15,69 +16,134 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
|
||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||
*/
|
||||
create or replace function createRbacRolesForHsOfficePerson()
|
||||
|
||||
create or replace procedure buildRbacSystemForHsOfficePerson(
|
||||
NEW hs_office_person
|
||||
)
|
||||
language plpgsql as $$
|
||||
|
||||
declare
|
||||
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
userUuids => array[currentUserUuid()],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_person row.
|
||||
*/
|
||||
|
||||
create or replace function insertTriggerForHsOfficePerson_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonReferrer(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
call buildRbacSystemForHsOfficePerson(NEW);
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
|
||||
create trigger createRbacRolesForHsOfficePerson_Trigger
|
||||
after insert
|
||||
on hs_office_person
|
||||
create trigger insertTriggerForHsOfficePerson_tg
|
||||
after insert on hs_office_person
|
||||
for each row
|
||||
execute procedure createRbacRolesForHsOfficePerson();
|
||||
execute procedure insertTriggerForHsOfficePerson_tf();
|
||||
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates INSERT INTO hs_office_person permissions for the related global rows.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
row global;
|
||||
permissionUuid uuid;
|
||||
roleUuid uuid;
|
||||
begin
|
||||
call defineContext('create INSERT INTO hs_office_person permissions for the related global rows');
|
||||
|
||||
FOR row IN SELECT * FROM global
|
||||
LOOP
|
||||
roleUuid := findRoleId(globalGuest());
|
||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person');
|
||||
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||
END LOOP;
|
||||
END;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Adds hs_office_person INSERT permission to specified role of new global rows.
|
||||
*/
|
||||
create or replace function hs_office_person_global_insert_tf()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
call grantPermissionToRole(
|
||||
globalGuest(),
|
||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_person'));
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger hs_office_person_global_insert_tg
|
||||
after insert on global
|
||||
for each row
|
||||
execute procedure hs_office_person_global_insert_tf();
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_person.
|
||||
*/
|
||||
create or replace function hs_office_person_insert_permission_missing_tf()
|
||||
returns trigger
|
||||
language plpgsql as $$
|
||||
begin
|
||||
raise exception '[403] insert into hs_office_person not allowed for current subjects % (%)',
|
||||
currentSubjects(), currentSubjectsUuids();
|
||||
end; $$;
|
||||
|
||||
--//
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call generateRbacIdentityViewFromProjection('hs_office_person', $idName$
|
||||
concat(target.tradeName, target.familyName, target.givenName)
|
||||
concat(tradeName, familyName, givenName)
|
||||
$idName$);
|
||||
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, target.familyName, target.givenName)',
|
||||
call generateRbacRestrictedView('hs_office_person',
|
||||
'concat(tradeName, familyName, givenName)',
|
||||
$updates$
|
||||
personType = new.personType,
|
||||
tradeName = new.tradeName,
|
||||
@ -87,48 +153,3 @@ call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, ta
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-person and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-person permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-person']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficePersonNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-person not permitted for %',
|
||||
array_to_string(currentSubjects(), ';', 'null');
|
||||
end; $$;
|
||||
|
||||
/**
|
||||
Checks if the user or assumed roles are allowed to create a new customer.
|
||||
*/
|
||||
create trigger hs_office_person_insert_trigger
|
||||
before insert
|
||||
on hs_office_person
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new persons
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficePersonNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
||||
|
@ -27,71 +27,77 @@ create or replace function hsOfficeRelationshipRbacRolesTrigger()
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
hsOfficeRelationshipTenant RbacRoleDescriptor;
|
||||
newRelAnchor hs_office_person;
|
||||
newRelHolder hs_office_person;
|
||||
newAnchorPerson hs_office_person;
|
||||
newHolderPerson hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
begin
|
||||
call enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
hsOfficeRelationshipTenant := hsOfficeRelationshipTenant(NEW);
|
||||
|
||||
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newRelAnchor;
|
||||
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newRelHolder;
|
||||
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newAnchorPerson;
|
||||
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newHolderPerson;
|
||||
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- cannot be generated using `tools/generate` because there are multiple grants to the same entity type
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[
|
||||
globalAdmin(),
|
||||
hsOfficePersonAdmin(newRelAnchor)]
|
||||
);
|
||||
globalAdmin()
|
||||
]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
|
||||
);
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeRelationshipOwner(NEW),
|
||||
hsOfficePersonAdmin(newAnchorPerson)
|
||||
]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipTenant,
|
||||
permissions => array['SELECT'],
|
||||
hsOfficeRelationshipAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
hsOfficePersonAdmin(newRelAnchor),
|
||||
hsOfficePersonAdmin(newRelHolder),
|
||||
hsOfficeContactAdmin(newContact)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePersonTenant(newRelAnchor),
|
||||
hsOfficePersonTenant(newRelHolder),
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
hsOfficePersonAdmin(newHolderPerson),
|
||||
hsOfficeContactAdmin(newContact)
|
||||
]
|
||||
);
|
||||
|
||||
-- anchor and holder admin roles need each others tenant role
|
||||
-- to be able to see the joined relationship
|
||||
-- TODO: this can probably be avoided through agent+guest roles
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
|
||||
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipTenant(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeRelationshipAgent(NEW)
|
||||
],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePersonReferrer(newAnchorPerson),
|
||||
hsOfficePersonReferrer(newHolderPerson),
|
||||
hsOfficeContactReferrer(newContact)
|
||||
]
|
||||
);
|
||||
|
||||
if ( NEW.relType = 'REPRESENTATIVE' ) then
|
||||
call grantRoleToRole(hsOfficePersonAdmin(newHolderPerson), hsOfficePersonAdmin(newAnchorPerson));
|
||||
end if;
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
-- nothing but the contact can be updated,
|
||||
-- only the contact can be updated,
|
||||
-- in other cases, a new relationship needs to be created and the old updated
|
||||
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
|
||||
call revokeRoleFromRole( hsOfficeContactReferrer(oldContact), hsOfficeRelationshipTenant(NEW) );
|
||||
call grantRoleToRole( hsOfficeContactReferrer(newContact), hsOfficeRelationshipTenant(NEW) );
|
||||
|
||||
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
|
||||
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
|
||||
call revokeRoleFromRole( hsOfficeRelationshipAgent(NEW), hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficeRelationshipAgent(NEW), hsOfficeContactAdmin(newContact) );
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
@ -136,8 +142,8 @@ call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
|
||||
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relationship',
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
|
||||
$updates$
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
@ -1,72 +1,158 @@
|
||||
### hs_office_partner RBAC
|
||||
### rbac partner
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T15:29:41.494727519.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph external[ ]
|
||||
style external fill:#fff
|
||||
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerPerson
|
||||
style partnerPerson fill:#eee
|
||||
|
||||
role:partnerPerson.admin[partnerPerson.admin]
|
||||
end
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
subgraph otherRelatedPerson
|
||||
style otherRelatedPerson fill:#eee
|
||||
|
||||
role:otherRelatedPerson.admin[otherRelatedPerson.admin]
|
||||
end
|
||||
|
||||
subgraph hsOfficeRelationship[hsOfficeRelationship:PARTNER]
|
||||
direction TB
|
||||
style hsOfficeRelationship fill:#eee
|
||||
|
||||
role:global.admin
|
||||
--> role:hsOfficeRelationship.owner[relationship.owner]
|
||||
--> role:hsOfficeRelationship.admin[relationship.admin]
|
||||
--> role:hsOfficeRelationship.agent[relationship.agent]
|
||||
--> role:hsOfficeRelationship.tenant[relationship.tenant]
|
||||
|
||||
role:partnerPerson.admin --> role:hsOfficeRelationship.agent
|
||||
role:otherRelatedPerson.admin --> role:hsOfficeRelationship.tenant
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph internal[ ]
|
||||
subgraph partner["`**partner**`"]
|
||||
direction TB
|
||||
style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph hsOfficePartner
|
||||
style hsOfficePartner fill:#fff
|
||||
|
||||
perm:hsOfficePartner.*{{partner.*}}
|
||||
role:hsOfficeRelationship.owner ==> perm:hsOfficePartner.*
|
||||
|
||||
perm:hsOfficePartner.edit{{partner.edit}}
|
||||
role:hsOfficeRelationship.admin ==> perm:hsOfficePartner.edit
|
||||
|
||||
perm:hsOfficePartner.view{{partner.view}}
|
||||
role:hsOfficeRelationship.tenant ==> perm:hsOfficePartner.view
|
||||
subgraph partner:permissions[ ]
|
||||
style partner:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:partner:new-partner{{partner:new-partner}}
|
||||
perm:partner:DELETE{{partner:DELETE}}
|
||||
perm:partner:UPDATE{{partner:UPDATE}}
|
||||
perm:partner:SELECT{{partner:SELECT}}
|
||||
end
|
||||
|
||||
subgraph hsOfficePartnerDetails
|
||||
subgraph partnerRel["`**partnerRel**`"]
|
||||
direction TB
|
||||
style hsOfficePartnerDetails fill:#eee
|
||||
|
||||
perm:hsOfficePartnerDetails.*{{partnerDetails.*}}
|
||||
role:hsOfficeRelationship.owner ==> perm:hsOfficePartnerDetails.*
|
||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
||||
direction TB
|
||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
perm:hsOfficePartnerDetails.edit{{partnerDetails.edit}}
|
||||
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.edit
|
||||
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.view
|
||||
|
||||
perm:hsOfficePartnerDetails.view{{partnerDetails.view}}
|
||||
subgraph partnerRel.contact:roles[ ]
|
||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel:roles[ ]
|
||||
style partnerRel:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel:owner[[partnerRel:owner]]
|
||||
role:partnerRel:admin[[partnerRel:admin]]
|
||||
role:partnerRel:agent[[partnerRel:agent]]
|
||||
role:partnerRel:tenant[[partnerRel:tenant]]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
subgraph partnerDetails["`**partnerDetails**`"]
|
||||
direction TB
|
||||
style partnerDetails fill:#feb28c,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerDetails:permissions[ ]
|
||||
style partnerDetails:permissions fill:#feb28c,stroke:white
|
||||
|
||||
perm:partnerDetails:DELETE{{partnerDetails:DELETE}}
|
||||
perm:partnerDetails:UPDATE{{partnerDetails:UPDATE}}
|
||||
perm:partnerDetails:SELECT{{partnerDetails:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.anchorPerson:roles[ ]
|
||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
||||
direction TB
|
||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph partnerRel.holderPerson:roles[ ]
|
||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
||||
|
||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:global:admin -.-> role:partnerRel.anchorPerson:owner
|
||||
role:partnerRel.anchorPerson:owner -.-> role:partnerRel.anchorPerson:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.holderPerson:owner
|
||||
role:partnerRel.holderPerson:owner -.-> role:partnerRel.holderPerson:admin
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel.holderPerson:referrer
|
||||
role:global:admin -.-> role:partnerRel.contact:owner
|
||||
role:partnerRel.contact:owner -.-> role:partnerRel.contact:admin
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel.contact:referrer
|
||||
role:global:admin -.-> role:partnerRel:owner
|
||||
role:partnerRel:owner -.-> role:partnerRel:admin
|
||||
role:partnerRel.anchorPerson:admin -.-> role:partnerRel:admin
|
||||
role:partnerRel:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:agent
|
||||
role:partnerRel:agent -.-> role:partnerRel:tenant
|
||||
role:partnerRel.holderPerson:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel.contact:admin -.-> role:partnerRel:tenant
|
||||
role:partnerRel:tenant -.-> role:partnerRel.anchorPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.holderPerson:referrer
|
||||
role:partnerRel:tenant -.-> role:partnerRel.contact:referrer
|
||||
|
||||
%% granting permissions to roles
|
||||
role:global:admin ==> perm:partner:new-partner
|
||||
role:partnerRel:admin ==> perm:partner:DELETE
|
||||
role:partnerRel:agent ==> perm:partner:UPDATE
|
||||
role:partnerRel:tenant ==> perm:partner:SELECT
|
||||
role:partnerRel:admin ==> perm:partnerDetails:DELETE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:UPDATE
|
||||
role:partnerRel:agent ==> perm:partnerDetails:SELECT
|
||||
|
||||
```
|
||||
|
@ -32,58 +32,7 @@ begin
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- === ATTENTION: code generated from related Mermaid flowchart: ===
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerOwner(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
permissions => array['UPDATE'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerOwner(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeRelationshipTenant(newPartnerRole),
|
||||
hsOfficePersonTenant(newPerson),
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
hsOfficeRelationshipAdmin(newPartnerRole),
|
||||
hsOfficePersonAdmin(newPerson),
|
||||
hsOfficeContactAdmin(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerTenant(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerAgent(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficeRelationshipTenant(newPartnerRole),
|
||||
hsOfficePersonGuest(newPerson),
|
||||
hsOfficeContactGuest(newContact)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerGuest(NEW),
|
||||
permissions => array['SELECT'],
|
||||
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
|
||||
);
|
||||
|
||||
-- === END of code generated from Mermaid flowchart. ===
|
||||
|
||||
-- Each partner-details entity belong exactly to one partner entity
|
||||
-- and it makes little sense just to delegate partner-details roles.
|
||||
-- Therefore, we did not model partner-details roles,
|
||||
-- but instead just assign extra permissions to existing partner-roles.
|
||||
|
||||
--Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
|
||||
-- Permissions and Grants for Partner
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
||||
@ -96,21 +45,21 @@ begin
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
|
||||
createPermissions(partnerUuid, array ['view'])
|
||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
|
||||
createPermissions(partnerUuid, array ['SELECT'])
|
||||
);
|
||||
|
||||
-- Permissions and Grants for PartnerDetails
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||
createPermissions(partnerDetailsUuid, array ['*'])
|
||||
);
|
||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
||||
createPermissions(partnerDetailsUuid, array ['DELETE'])
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||
createPermissions(partnerDetailsUuid, array ['edit'])
|
||||
);
|
||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
||||
createPermissions(partnerDetailsUuid, array ['UPDATE'])
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT.
|
||||
@ -118,7 +67,7 @@ begin
|
||||
-- Otherwise package-admins etc. would be able to read the data.
|
||||
getRoleId(hsOfficeRelationshipAgent(newPartnerRel)),
|
||||
createPermissions(partnerDetailsUuid, array ['SELECT'])
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
@ -129,72 +78,72 @@ begin
|
||||
-- Revokes from Partner
|
||||
|
||||
call revokePermissionFromRole(
|
||||
findPermissionId(partnerUuid, 'view'),
|
||||
findPermissionId(partnerUuid, 'SELECT'),
|
||||
hsOfficeRelationshipTenant(oldPartnerRel)
|
||||
);
|
||||
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerUuid, 'edit'),
|
||||
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
||||
-- );
|
||||
--
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerUuid, '*'),
|
||||
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
||||
-- );
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerUuid, 'edit'),
|
||||
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
||||
-- );
|
||||
--
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerUuid, '*'),
|
||||
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
||||
-- );
|
||||
|
||||
-- Grants for Partner
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||
array[findPermissionId(partnerUuid, '*')]
|
||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
||||
array[findPermissionId(partnerUuid, 'DELETE')]
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||
array[findPermissionId(partnerUuid, 'edit')]
|
||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
||||
array[findPermissionId(partnerUuid, 'UPDATE')]
|
||||
);
|
||||
|
||||
call grantPermissionsToRole(
|
||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
|
||||
array[findPermissionId(partnerUuid, 'view')]
|
||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
|
||||
array[findPermissionId(partnerUuid, 'SELECT')]
|
||||
);
|
||||
|
||||
-- Revokes from PartnerDetails
|
||||
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerDetailsUuid, 'view'),
|
||||
-- hsOfficeRelationshipAgent(oldPartnerRel)
|
||||
-- );
|
||||
--
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerDetailsUuid, 'edit'),
|
||||
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
||||
-- );
|
||||
--
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerDetailsUuid, '*'),
|
||||
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
||||
-- );
|
||||
-- call revokePermissionFromRole(
|
||||
-- findPermissionId(partnerDetailsUuid, 'SELECT'),
|
||||
-- hsOfficeRelationshipAgent(oldPartnerRel)
|
||||