diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java index 30c8c45f..1dc8e39f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java @@ -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"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java index a0703ce3..7770608a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java @@ -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"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index 16dde623..a048fe4d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -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"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java index 5303c27e..085a1999 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java @@ -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 + ")"; + } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java index d664a83b..7e3c6a3b 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java @@ -31,7 +31,7 @@ public class RbacIdentityViewGenerator { $idName$); """; case SQL_QUERY -> """ - call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$ + call generateRbacIdentityViewFromQuery('${rawTableName}', $idName$ ${identityViewSqlPart} $idName$); """; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java index f8f6e890..a2d53d39 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java @@ -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$); --// diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java index 28d29365..e2c06515 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java @@ -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(); } } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java index 211c8b43..9dfaea74 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java @@ -103,7 +103,7 @@ public class RbacGrantController implements RbacGrantsApi { // public ResponseEntity 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); // } diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidService.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidService.java deleted file mode 100644 index 7ddefbb6..00000000 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidService.java +++ /dev/null @@ -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 includes) { - final var graph = new HashSet(); - for ( UUID subjectUuid: context.currentSubjectsUuids() ) { - traverseGrantsTo(graph, subjectUuid, includes); - } - return toMermaidFlowchart(graph, includes); - } - - private void traverseGrantsTo(final Set graph, final UUID refUuid, final EnumSet 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 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(); - traverseGrantsFrom(graph, refUuid, includes); - return toMermaidFlowchart(graph, includes); - } - - private void traverseGrantsFrom(final Set graph, final UUID refUuid, final EnumSet 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 graph, final EnumSet 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) { - -} diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java index 99b0fb3c..f0a0827c 100644 --- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java @@ -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(); diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index 4bbc1679..fd0983bd 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -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:--// -- ---------------------------------------------------------------------------- diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql index 8313d05d..f8058113 100644 --- a/src/main/resources/db/changelog/080-rbac-global.sql +++ b/src/main/resources/db/changelog/080-rbac-global.sql @@ -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:--// diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql index 4b58a0af..f83d2dde 100644 --- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql @@ -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(); ---// - diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql index 126664a4..27e02ee3 100644 --- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql @@ -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$); --// diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md index 86e12c29..8c321664 100644 --- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md @@ -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 + ``` diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql index 072bcf19..c91e15b1 100644 --- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql @@ -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) + -- ); + -- + -- call revokePermissionFromRole( + -- findPermissionId(partnerDetailsUuid, 'UPDATE'), + -- hsOfficeRelationshipAdmin(oldPartnerRel) + -- ); + -- + -- call revokePermissionFromRole( + -- findPermissionId(partnerDetailsUuid, 'DELETE'), + -- hsOfficeRelationshipOwner(oldPartnerRel) + -- ); -- Grants for PartnerDetails call grantPermissionsToRole( - getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'), - array[findPermissionId(partnerDetailsUuid, '*')] + getRoleId(hsOfficeRelationshipOwner(newPartnerRel)), + array[findPermissionId(partnerDetailsUuid, 'DELETE')] ); call grantPermissionsToRole( - getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'), - array[findPermissionId(partnerDetailsUuid, 'edit')] + getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)), + array[findPermissionId(partnerDetailsUuid, 'UPDATE')] ); call grantPermissionsToRole( -- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT. -- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT! -- Otherwise package-admins etc. would be able to read the data. - getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'), - array[findPermissionId(partnerDetailsUuid, 'view')] + getRoleId(hsOfficeRelationshipAgent(newPartnerRel)), + array[findPermissionId(partnerDetailsUuid, 'SELECT')] ); end if; @@ -203,7 +152,7 @@ begin raise exception 'invalid usage of TRIGGER'; end if; - call leaveTriggerForObjectUuid(NEW.uuid); + call leaveTriggerForObjectUuid(partnerUuid); return NEW; end; $$; diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md index 4df5a36d..e4c25d7c 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md @@ -1,50 +1,45 @@ -### hs_office_bankaccount RBAC Roles +### rbac bankAccount + +This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T19:09:38.350576842. ```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% flowchart TB -%%% RbacEntity.builder().forEntity(HsOfficeBankAccountEntity.class) -%%% .alias("bankAccount") -%% the global subgraph would get imported implicitly by later usage -subgraph global - style global fill: #eee - - role:global.admin[global.admin] -end - -subgraph hsOfficeBankAccount - +subgraph bankAccount["`**bankAccount**`"] direction TB - style hsOfficeBankAccount fill: #e9f7ef + style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px - user:hsOfficeBankAccount.creator([bankAccount.creator]) + subgraph bankAccount:roles[ ] + style bankAccount:roles fill:#dd4901,stroke:white - %%% .createRole(OWNER) - role:hsOfficeBankAccount.owner[[bankAccount.owner]] - %%% .withPermission(ALL) - %% permissions - role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{hsOfficeBankAccount.*}} - %% incoming - %%% .withCreatorAsOwningUser() - user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner - %%% .withIncomingSuperRole(GlobalEntity.class, ADMIN) - role:global.admin --> role:hsOfficeBankAccount.owner - - %%% .createSubRole(ADMIN) - role:hsOfficeBankAccount.admin[[bankAccount.admin]] - %% permissions - %%% .withPermission(EDIT) - role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{hsOfficeBankAccount.edit}} - %% incoming - role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin + role:bankAccount:owner[[bankAccount:owner]] + role:bankAccount:admin[[bankAccount:admin]] + role:bankAccount:referrer[[bankAccount:referrer]] + end - %%% .createSubRole(REFERRER) - role:hsOfficeBankAccount.referrer[[bankAccount.referrer]] - %% permissions - %%% .withPermission(VIEW) - role:hsOfficeBankAccount.referrer --> perm:hsOfficeBankAccount.view{{hsOfficeBankAccount.view}} - %% incoming - role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.referrer + subgraph bankAccount:permissions[ ] + style bankAccount:permissions fill:#dd4901,stroke:white + + perm:bankAccount:INSERT{{bankAccount:INSERT}} + perm:bankAccount:DELETE{{bankAccount:DELETE}} + perm:bankAccount:UPDATE{{bankAccount:UPDATE}} + perm:bankAccount:SELECT{{bankAccount:SELECT}} + end end -``` +%% granting roles to users +user:creator ==> role:bankAccount:owner + +%% granting roles to roles +role:global:admin ==> role:bankAccount:owner +role:bankAccount:owner ==> role:bankAccount:admin +role:bankAccount:admin ==> role:bankAccount:referrer + +%% granting permissions to roles +role:global:guest ==> perm:bankAccount:INSERT +role:bankAccount:owner ==> perm:bankAccount:DELETE +role:bankAccount:admin ==> perm:bankAccount:UPDATE +role:bankAccount:referrer ==> perm:bankAccount:SELECT + +``` diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql index b06bb3a0..74a95ce2 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql @@ -1,4 +1,5 @@ --liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T19:09:38.359318650. -- ============================================================================ --changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--// @@ -15,125 +16,141 @@ call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount') -- ============================================================================ ---changeset hs-office-bankaccount-rbac-ROLES-CREATION:1 endDelimiter:--// +--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* - Creates the roles and their assignments for a new bankaccount for the AFTER INSERT TRIGGER. + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. */ -create or replace function createRbacRolesForHsOfficeBankAccount() +create or replace procedure buildRbacSystemForHsOfficeBankAccount( + NEW hs_office_bankaccount +) + language plpgsql as $$ + +declare + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + perform createRoleWithGrants( + hsOfficeBankAccountOwner(NEW), + permissions => array['DELETE'], + userUuids => array[currentUserUuid()], + incomingSuperRoles => array[globalAdmin()] + ); + + perform createRoleWithGrants( + hsOfficeBankAccountAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeBankAccountReferrer(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_bankaccount row. + */ + +create or replace function insertTriggerForHsOfficeBankAccount_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( - hsOfficeBankAccountOwner(NEW), - permissions => array['DELETE'], - incomingSuperRoles => array[globalAdmin()], - userUuids => array[currentUserUuid()], - grantedByRole => globalAdmin() - ); - - perform createRoleWithGrants( - hsOfficeBankAccountAdmin(NEW), - incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)] - ); - - perform createRoleWithGrants( - hsOfficeBankAccountTenant(NEW), - incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)] - ); - - perform createRoleWithGrants( - hsOfficeBankAccountGuest(NEW), - permissions => array['SELECT'], - incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)] - ); - + call buildRbacSystemForHsOfficeBankAccount(NEW); return NEW; end; $$; -/* - An AFTER INSERT TRIGGER which creates the role structure for a new customer. - */ - -create trigger createRbacRolesForHsOfficeBankAccount_Trigger - after insert - on hs_office_bankaccount +create trigger insertTriggerForHsOfficeBankAccount_tg + after insert on hs_office_bankaccount for each row -execute procedure createRbacRolesForHsOfficeBankAccount(); +execute procedure insertTriggerForHsOfficeBankAccount_tf(); + --// +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/* + Creates INSERT INTO hs_office_bankaccount permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_bankaccount permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalGuest()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'); + call grantPermissionToRole(roleUuid, permissionUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_bankaccount INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_bankaccount_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + globalGuest(), + createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount')); + return NEW; +end; $$; + +create trigger hs_office_bankaccount_global_insert_tg + after insert on global + for each row +execute procedure hs_office_bankaccount_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount. +*/ +create or replace function hs_office_bankaccount_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +--// -- ============================================================================ --changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$ - target.iban || ':' || target.holder + concat(iban, ':', holder) $idName$); + --// - - -- ============================================================================ --changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacRestrictedView('hs_office_bankaccount', 'target.holder', +call generateRbacRestrictedView('hs_office_bankaccount', + $orderBy$ + concat(iban, ':', holder) + $orderBy$, $updates$ - holder = new.holder, + holder = new.holder, iban = new.iban, bic = new.bic $updates$); ---/ - - --- ============================================================================ ---changeset hs-office-bankaccount-rbac-NEW-BANKACCOUNT:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -/* - Creates a global permission for new-bankaccount and assigns it to the hostsharing admins role. - */ -do language plpgsql $$ - declare - addCustomerPermissions uuid[]; - globalObjectUuid uuid; - globalAdminRoleUuid uuid ; - begin - call defineContext('granting global new-bankaccount permission to global admin role', null, null, null); - - globalAdminRoleUuid := findRoleId(globalAdmin()); - globalObjectUuid := (select uuid from global); - addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-bankaccount']); - call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions); - end; -$$; - -/** - Used by the trigger to prevent the add-customer to current user respectively assumed roles. - */ -create or replace function addHsOfficeBankAccountNotAllowedForCurrentSubjects() - returns trigger - language PLPGSQL -as $$ -begin - raise exception '[403] new-bankaccount 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_bankaccount_insert_trigger - before insert - on hs_office_bankaccount - for each row - -- TODO.spec: who is allowed to create new bankaccounts - when ( not hasAssumedRole() ) -execute procedure addHsOfficeBankAccountNotAllowedForCurrentSubjects(); --// + diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md index 3cc8b9fa..5381bcd6 100644 --- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md @@ -1,62 +1,178 @@ -### hs_office_sepaMandate RBAC +### rbac sepaMandate + +This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T18:29:47.084556363. ```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% flowchart TB -subgraph global - style global fill:#eee - - role:global.admin[global.admin] -end - -subgraph hsOfficeBankAccount +subgraph bankAccount["`**bankAccount**`"] direction TB - style hsOfficeBankAccount fill:#eee - - role:hsOfficeBankAccount.owner[bankAccount.owner] - --> role:hsOfficeBankAccount.admin[bankAccount.admin] - --> role:hsOfficeBankAccount.referrer[bankAccount.referrer] + style bankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bankAccount:roles[ ] + style bankAccount:roles fill:#99bcdb,stroke:white + + role:bankAccount:owner[[bankAccount:owner]] + role:bankAccount:admin[[bankAccount:admin]] + role:bankAccount:referrer[[bankAccount:referrer]] + end end -subgraph hsOfficeRelationship:DEBITOR +subgraph debitorRel.contact["`**debitorRel.contact**`"] direction TB - style hsOfficeRelationship:DEBITOR fill:#eee - - role:hsOfficeRelationship:DEBITOR.owner[debitorRel.owner] - --> role:hsOfficeRelationship:DEBITOR.admin[debitorRel.admin] - --> role:hsOfficeRelationship:DEBITOR.agent[debitorRel.agent] - --> role:hsOfficeRelationship:DEBITOR.tenant[debitorRel.tenant] + style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.contact:roles[ ] + style debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:debitorRel.contact:owner[[debitorRel.contact:owner]] + role:debitorRel.contact:admin[[debitorRel.contact:admin]] + role:debitorRel.contact:referrer[[debitorRel.contact:referrer]] + end end -subgraph hsOfficeSepaMandate - - role:hsOfficeSepaMandate.owner[sepaMandate.owner] - %% permissions - role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}} - %% incoming - role:global.admin ---> role:hsOfficeSepaMandate.owner - - role:hsOfficeSepaMandate.admin[sepaMandate.admin] - %% permissions - role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}} - %% incoming - role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin - - role:hsOfficeSepaMandate.agent[sepaMandate.agent] - %% incoming - role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent - role:hsOfficeRelationship:DEBITOR.admin --> role:hsOfficeSepaMandate.agent - role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent - %% outgoing - role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.referrer - role:hsOfficeSepaMandate.agent --> role:hsOfficeRelationship:DEBITOR.tenant - role:hsOfficeSepaMandate.agent --> role:hsOfficeBankAccount.referrer - - role:hsOfficeSepaMandate.tenant[sepaMandate.tenant] - %% incoming - role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant +subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"] + direction TB + style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + subgraph debitorRel.anchorPerson:roles[ ] + style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]] + role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]] + role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]] + end end +subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"] + direction TB + style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.holderPerson:roles[ ] + style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]] + role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]] + role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]] + end +end + +subgraph sepaMandate["`**sepaMandate**`"] + direction TB + style sepaMandate fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph sepaMandate:roles[ ] + style sepaMandate:roles fill:#dd4901,stroke:white + + role:sepaMandate:owner[[sepaMandate:owner]] + role:sepaMandate:admin[[sepaMandate:admin]] + role:sepaMandate:agent[[sepaMandate:agent]] + role:sepaMandate:referrer[[sepaMandate:referrer]] + end + + subgraph sepaMandate:permissions[ ] + style sepaMandate:permissions fill:#dd4901,stroke:white + + perm:sepaMandate:DELETE{{sepaMandate:DELETE}} + perm:sepaMandate:UPDATE{{sepaMandate:UPDATE}} + perm:sepaMandate:SELECT{{sepaMandate:SELECT}} + end +end + +subgraph debitorRel["`**debitorRel**`"] + direction TB + style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.contact["`**debitorRel.contact**`"] + direction TB + style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.contact:roles[ ] + style debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:debitorRel.contact:owner[[debitorRel.contact:owner]] + role:debitorRel.contact:admin[[debitorRel.contact:admin]] + role:debitorRel.contact:referrer[[debitorRel.contact:referrer]] + end + end + + subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"] + direction TB + style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.anchorPerson:roles[ ] + style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]] + role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]] + role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]] + end + end + + subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"] + direction TB + style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph debitorRel.holderPerson:roles[ ] + style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]] + role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]] + role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]] + end + end + + subgraph debitorRel:roles[ ] + style debitorRel:roles fill:#99bcdb,stroke:white + + role:debitorRel:owner[[debitorRel:owner]] + role:debitorRel:admin[[debitorRel:admin]] + role:debitorRel:agent[[debitorRel:agent]] + role:debitorRel:tenant[[debitorRel:tenant]] + end +end + +%% granting roles to users +user:creator ==> role:sepaMandate:owner + +%% granting roles to roles +role:global:admin -.-> role:debitorRel.anchorPerson:owner +role:debitorRel.anchorPerson:owner -.-> role:debitorRel.anchorPerson:admin +role:debitorRel.anchorPerson:admin -.-> role:debitorRel.anchorPerson:referrer +role:global:admin -.-> role:debitorRel.holderPerson:owner +role:debitorRel.holderPerson:owner -.-> role:debitorRel.holderPerson:admin +role:debitorRel.holderPerson:admin -.-> role:debitorRel.holderPerson:referrer +role:global:admin -.-> role:debitorRel.contact:owner +role:debitorRel.contact:owner -.-> role:debitorRel.contact:admin +role:debitorRel.contact:admin -.-> role:debitorRel.contact:referrer +role:global:admin -.-> role:debitorRel:owner +role:debitorRel:owner -.-> role:debitorRel:admin +role:debitorRel.anchorPerson:admin -.-> role:debitorRel:admin +role:debitorRel:admin -.-> role:debitorRel:agent +role:debitorRel.holderPerson:admin -.-> role:debitorRel:agent +role:debitorRel:agent -.-> role:debitorRel:tenant +role:debitorRel.holderPerson:admin -.-> role:debitorRel:tenant +role:debitorRel.contact:admin -.-> role:debitorRel:tenant +role:debitorRel:tenant -.-> role:debitorRel.anchorPerson:referrer +role:debitorRel:tenant -.-> role:debitorRel.holderPerson:referrer +role:debitorRel:tenant -.-> role:debitorRel.contact:referrer +role:global:admin -.-> role:bankAccount:owner +role:bankAccount:owner -.-> role:bankAccount:admin +role:bankAccount:admin -.-> role:bankAccount:referrer +role:global:admin ==> role:sepaMandate:owner +role:sepaMandate:owner ==> role:sepaMandate:admin +role:sepaMandate:admin ==> role:sepaMandate:agent +role:sepaMandate:agent ==> role:bankAccount:referrer +role:sepaMandate:agent ==> role:debitorRel:agent +role:sepaMandate:agent ==> role:sepaMandate:referrer +role:bankAccount:admin ==> role:sepaMandate:referrer +role:debitorRel:agent ==> role:sepaMandate:referrer +role:sepaMandate:referrer ==> role:debitorRel:tenant + +%% granting permissions to roles +role:sepaMandate:owner ==> perm:sepaMandate:DELETE +role:sepaMandate:admin ==> perm:sepaMandate:UPDATE +role:sepaMandate:referrer ==> perm:sepaMandate:SELECT ``` diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql index 3d0545bf..aedfb28d 100644 --- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql @@ -1,4 +1,5 @@ --liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T18:29:47.095199204. -- ============================================================================ --changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--// @@ -15,105 +16,123 @@ call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate') -- ============================================================================ ---changeset hs-office-sepamandate-rbac-ROLES-CREATION:1 endDelimiter:--// +--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* - Creates and updates the roles and their assignments for sepaMandate entities. + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. */ -create or replace function hsOfficeSepaMandateRbacRolesTrigger() - returns trigger - language plpgsql - strict as $$ +create or replace procedure buildRbacSystemForHsOfficeSepaMandate( + NEW hs_office_sepamandate +) + language plpgsql as $$ + declare - newHsOfficeDebitor hs_office_debitor; - newhsOfficeRelationship:DEBITOR hs_office_relationship; - newHsOfficeBankAccount hs_office_bankAccount; + newBankAccount hs_office_bankaccount; + newDebitorRel hs_office_relationship; + begin call enterTriggerForObjectUuid(NEW.uuid); + SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid into newBankAccount; + SELECT * FROM hs_office_relationship WHERE uuid = NEW.debitorUuid into newDebitorRel; - select * from hs_office_debitor as p where p.uuid = NEW.debitorUuid into newHsOfficeDebitor; - select * from hs_office_relationship as r where r.uuid = newHsOfficeDebitor.debitorRelUuid into newhsOfficeRelationship:DEBITOR; - select * from hs_office_bankAccount as c where c.uuid = NEW.bankAccountUuid into newHsOfficeBankAccount; + perform createRoleWithGrants( + hsOfficeSepaMandateOwner(NEW), + permissions => array['DELETE'], + userUuids => array[currentUserUuid()], + incomingSuperRoles => array[globalAdmin()] + ); - if TG_OP = 'INSERT' then + perform createRoleWithGrants( + hsOfficeSepaMandateAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)] + ); - -- === ATTENTION: code generated from related Mermaid flowchart: === + perform createRoleWithGrants( + hsOfficeSepaMandateAgent(NEW), + incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)], + outgoingSubRoles => array[ + hsOfficeBankAccountReferrer(newBankAccount), + hsOfficeRelationshipAgent(newDebitorRel)] + ); - perform createRoleWithGrants( - hsOfficeSepaMandateOwner(NEW), - permissions => array['DELETE'], - incomingSuperRoles => array[globalAdmin()] - ); - - perform createRoleWithGrants( - hsOfficeSepaMandateAdmin(NEW), - permissions => array['UPDATE'], - incomingSuperRoles => array[ - hsOfficeSepaMandateOwner(NEW)], - outgoingSubRoles => array[ - hsOfficeBankAccountTenant(newHsOfficeBankAccount)] - ); - - perform createRoleWithGrants( - hsOfficeSepaMandateAgent(NEW), - incomingSuperRoles => array[ - hsOfficeSepaMandateAdmin(NEW), - hsOfficeRelationshipAdmin(newhsOfficeRelationship:DEBITOR), - hsOfficeBankAccountAdmin(newHsOfficeBankAccount)], - outgoingSubRoles => array[ - hsOfficeRelationshipTenant(newhsOfficeRelationship:DEBITOR)] - ); - - perform createRoleWithGrants( - hsOfficeSepaMandateTenant(NEW), - incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)], - outgoingSubRoles => array[ - hsOfficeRelationshipReferrer(newhsOfficeRelationship:DEBITOR), - hsOfficeBankAccountGuest(newHsOfficeBankAccount)] - ); - - perform createRoleWithGrants( - hsOfficeSepaMandateGuest(NEW), - permissions => array['SELECT'], - incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)] - ); - - -- === END of code generated from Mermaid flowchart. === - - else - raise exception 'invalid usage of TRIGGER'; - end if; + perform createRoleWithGrants( + hsOfficeSepaMandateReferrer(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[ + hsOfficeRelationshipAgent(newDebitorRel), + hsOfficeBankAccountAdmin(newBankAccount), + hsOfficeSepaMandateAgent(NEW)], + outgoingSubRoles => array[hsOfficeRelationshipTenant(newDebitorRel)] + ); call leaveTriggerForObjectUuid(NEW.uuid); - return NEW; end; $$; /* - An AFTER INSERT TRIGGER which creates the role structure for a new customer. + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row. */ -create trigger createRbacRolesForHsOfficeSepaMandate_Trigger - after insert - on hs_office_sepamandate + +create or replace function insertTriggerForHsOfficeSepaMandate_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficeSepaMandate(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficeSepaMandate_tg + after insert on hs_office_sepamandate for each row -execute procedure hsOfficeSepaMandateRbacRolesTrigger(); +execute procedure insertTriggerForHsOfficeSepaMandate_tf(); + --// +-- ============================================================================ +--changeset hs-office-sepamandate-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate. +*/ +create or replace function hs_office_sepamandate_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_sepamandate_insert_permission_check_tg + before insert on hs_office_sepamandate + for each row + -- As there is no explicit INSERT grant specified for this table, + -- only global admins are allowed to insert any rows. + when ( not isGlobalAdmin() ) + execute procedure hs_office_sepamandate_insert_permission_missing_tf(); + +--// -- ============================================================================ --changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacIdentityViewFromProjection('hs_office_sepamandate', 'target.reference'); + + call generateRbacIdentityViewFromQuery('hs_office_sepamandate', $idName$ + 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 + + $idName$); + --// - - -- ============================================================================ --changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacRestrictedView('hs_office_sepamandate', - orderby => 'target.reference', - columnUpdates => $updates$ + 'validity', + $updates$ reference = new.reference, agreement = new.agreement, validity = new.validity @@ -121,48 +140,3 @@ call generateRbacRestrictedView('hs_office_sepamandate', --// --- ============================================================================ ---changeset hs-office-sepamandate-rbac-NEW-SepaMandate:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -/* - Creates a global permission for new-sepaMandate and assigns it to the hostsharing admins role. - */ -do language plpgsql $$ - declare - addCustomerPermissions uuid[]; - globalObjectUuid uuid; - globalAdminRoleUuid uuid ; - begin - call defineContext('granting global new-sepaMandate permission to global admin role', null, null, null); - - globalAdminRoleUuid := findRoleId(globalAdmin()); - globalObjectUuid := (select uuid from global); - addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-sepamandate']); - call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions); - end; -$$; - -/** - Used by the trigger to prevent the add-customer to current user respectively assumed roles. - */ -create or replace function addHsOfficeSepaMandateNotAllowedForCurrentSubjects() - returns trigger - language PLPGSQL -as $$ -begin - raise exception '[403] new-sepaMandate 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_sepamandate_insert_trigger - before insert - on hs_office_sepamandate - for each row - -- TODO.spec: who is allowed to create new sepaMandates - when ( not hasAssumedRole() ) -execute procedure addHsOfficeSepaMandateNotAllowedForCurrentSubjects(); ---// - diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index 811b90d5..74b6a82d 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -50,25 +50,21 @@ begin -- Permissions and Grants for Debitor -- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'), +-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)), -- createPermissions(partnerUuid, array ['DELETE']) -- ); -- -- call grantPermissionsToRole( -- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'), --- createPermissions(partnerUuid, array ['edit']) +-- createPermissions(partnerUuid, array ['UPDATE']) -- ); -- -- call grantPermissionsToRole( -- getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'), --- createPermissions(partnerUuid, array ['view']) +-- createPermissions(partnerUuid, array ['SELECT']) -- ); - perform createRoleWithGrants( - hsOfficeDebitorAdmin(NEW), - permissions => array['UPDATE'], - incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)] - ); + -- Grants to and from related Partner Relationship -- call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true); -- call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true); @@ -78,12 +74,10 @@ begin -- Grants to and from refundBankAccount - perform createRoleWithGrants( - hsOfficeDebitorGuest(NEW), - permissions => array['SELECT'], - incomingSuperRoles => array[ - hsOfficeDebitorTenant(NEW)] - ); +-- if newBankAccount is not null then +-- call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true); +-- call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true); +-- end if; elsif TG_OP = 'UPDATE' then @@ -93,18 +87,18 @@ begin from hs_office_relationship as r where r.relType = 'ACCOUNTING' and r.relHolderUuid = NEW.debitorRelUuid; -- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'), --- createPermissions(partnerUuid, array ['*']) +-- getRoleId(hsOfficeRelationshipOwner(newDebitorRel)), +-- createPermissions(partnerUuid, array ['DELETE']) -- ); -- -- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'), --- createPermissions(partnerUuid, array ['edit']) +-- getRoleId(hsOfficeRelationshipAdmin(newDebitorRel)), +-- createPermissions(partnerUuid, array ['UPDATE']) -- ); -- -- call grantPermissionsToRole( --- getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'), --- createPermissions(partnerUuid, array ['view']) +-- getRoleId(hsOfficeRelationshipTenant(newDebitorRel)), +-- createPermissions(partnerUuid, array ['SELECT']) -- ); end if; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index 97ec6924..4118571d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -9,8 +9,8 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository; -import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService; -import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include; +import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService; +import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository; import net.hostsharing.test.Array; import net.hostsharing.test.JpaAttempt; @@ -40,7 +40,7 @@ import static net.hostsharing.test.JpaAttempt.attempt; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest -@Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class }) +@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class }) class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup { @Autowired @@ -71,7 +71,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean JpaAttempt jpaAttempt; @Autowired - RbacGrantsMermaidService mermaidService; + RbacGrantsDiagramService mermaidService; @MockBean HttpServletRequest request; @@ -315,7 +315,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif"); - RbacGrantsMermaidService.writeToFile("initial partner: Fourth eG + fourth contact", + RbacGrantsDiagramService.writeToFile("initial partner: Fourth eG + fourth contact", mermaidService.allGrantsFrom(givenDebitor.getUuid(), "view", EnumSet.of(Include.USERS, Include.DETAILS)), "doc/all-grants-before-globalAdmin_canUpdateArbitraryDebitor.md"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index c43a3675..6bfd513e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -465,24 +465,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); - final var partnerRole = HsOfficeRelationshipEntity.builder() - .relHolder(givenPartnerPerson) - .relType(HsOfficeRelationshipType.PARTNER) - .relAnchor(givenMandantorPerson) - .contact(givenContact) - .build(); - relationshipRepo.save(partnerRole); - - final var newPartner = HsOfficePartnerEntity.builder() - .partnerNumber(partnerNumber) - .partnerRole(partnerRole) - .person(givenPartnerPerson) - .contact(givenContact) - .details(HsOfficePartnerDetailsEntity.builder().build()) - .build(); - - return partnerRepo.save(newPartner); - }).assertSuccessful().returnedValue(); + final var partnerRole = HsOfficeRelationshipEntity.builder() + .relHolder(givenPartnerPerson) + .relType(HsOfficeRelationshipType.PARTNER) + .relAnchor(givenMandantorPerson) + .contact(givenContact) + .build(); + relationshipRepo.save(partnerRole); + return partnerRole; } void exactlyThesePartnersAreReturned(final List actualResult, final String... partnerNames) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java index 2dd2deff..f92fea7b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java @@ -59,7 +59,6 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu final var count = personRepo.count(); // when - final var result = attempt(em, () -> toCleanup(personRepo.save( hsOfficePerson("a new person")))); diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidServiceIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidServiceIntegrationTest.java deleted file mode 100644 index 1afde73d..00000000 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsMermaidServiceIntegrationTest.java +++ /dev/null @@ -1,136 +0,0 @@ -package net.hostsharing.hsadminng.rbac.rbacgrant; - -import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup; -import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include; -import net.hostsharing.test.JpaAttempt; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.EnumSet; -import java.util.UUID; - -import static java.lang.String.join; -import static org.assertj.core.api.Assertions.assertThat; - -@DataJpaTest -@Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class}) -class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanup { - - @Autowired - RbacGrantsMermaidService grantsMermaidService; - - @MockBean - HttpServletRequest request; - - @Test - void allGrantsToCurrentUser() { - context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner"); - final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES)); - - assertThat(graph).isEqualTo(""" - flowchart TB - - role:test_package#xxx00.tenant[ - test_package - xxx00.t - tenant] --> role:test_customer#xxx.tenant[ - test_customer - xxx.t - tenant] - role:test_domain#xxx00-aaaa.owner[ - test_domain - xxx00-aaaa.o - owner] --> role:test_domain#xxx00-aaaa.admin[ - test_domain - xxx00-aaaa.a - admin] - role:test_domain#xxx00-aaaa.admin[ - test_domain - xxx00-aaaa.a - admin] --> role:test_package#xxx00.tenant[ - test_package - xxx00.t - tenant] - """.trim()); - } - - @Test - void allGrantsToCurrentUserIncludingPermissions() { - context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner"); - final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES, Include.PERMISSIONS)); - - assertThat(graph).isEqualTo(""" - flowchart TB - - role:test_domain#xxx00-aaaa.owner[ - test_domain - xxx00-aaaa.o - owner] --> perm:*:on:test_domain#xxx00-aaaa{{ - test_domain - xxx00-aaaa - *}} - role:test_customer#xxx.tenant[ - test_customer - xxx.t - tenant] --> perm:view:on:test_customer#xxx{{ - test_customer - xxx - view}} - role:test_domain#xxx00-aaaa.admin[ - test_domain - xxx00-aaaa.a - admin] --> perm:edit:on:test_domain#xxx00-aaaa{{ - test_domain - xxx00-aaaa - edit}} - role:test_package#xxx00.tenant[ - test_package - xxx00.t - tenant] --> role:test_customer#xxx.tenant[ - test_customer - xxx.t - tenant] - role:test_domain#xxx00-aaaa.owner[ - test_domain - xxx00-aaaa.o - owner] --> role:test_domain#xxx00-aaaa.admin[ - test_domain - xxx00-aaaa.a - admin] - role:test_package#xxx00.tenant[ - test_package - xxx00.t - tenant] --> perm:view:on:test_package#xxx00{{ - test_package - xxx00 - view}} - role:test_domain#xxx00-aaaa.admin[ - test_domain - xxx00-aaaa.a - admin] --> role:test_package#xxx00.tenant[ - test_package - xxx00.t - tenant] - """.trim()); - } - - @Test -// @Disabled - void print() throws IOException { - //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin"); - context("superuser-alex@hostsharing.net"); - - //final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS)); - - final var targetObject = (UUID) em.createNativeQuery("SELECT uuid FROM hs_office_coopassetstransaction WHERE reference='ref 1000101-1'").getSingleResult(); - final var graph = grantsMermaidService.allGrantsFrom(targetObject, "view", EnumSet.of(Include.USERS)); - - RbacGrantsMermaidService.writeToFile(join(";", context.getAssumedRoles()), graph, "doc/all-grants.md"); - } -} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index a4f570f9..01e283b9 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,8 +4,8 @@ spring: platform: postgres datasource: - url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers - url-local: jdbc:postgresql://localhost:5432/postgres + url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers + url: jdbc:postgresql://localhost:5432/postgres username: postgres password: password