diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java index a7923128..e1f80148 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java @@ -70,6 +70,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi { final var entityToSave = new HsOfficeRelationEntity(); entityToSave.setType(HsOfficeRelationType.valueOf(body.getType())); + entityToSave.setMark(body.getMark()); entityToSave.setAnchor(holderRepo.findByUuid(body.getAnchorUuid()).orElseThrow( () -> new NoSuchElementException("cannot find anchorUuid " + body.getAnchorUuid()) )); diff --git a/src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml index b092cd0a..7b316b40 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml @@ -55,6 +55,7 @@ components: nullable: true mark: type: string + nullable: true contactUuid: type: string format: uuid @@ -62,4 +63,4 @@ components: - anchorUuid - holderUuid - type - - relContactUuid + - contactUuid diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md new file mode 100644 index 00000000..f3547312 --- /dev/null +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md @@ -0,0 +1,43 @@ +### rbac contact + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph contact["`**contact**`"] + direction TB + style contact fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph contact:roles[ ] + style contact:roles fill:#dd4901,stroke:white + + role:contact:owner[[contact:owner]] + role:contact:admin[[contact:admin]] + role:contact:referrer[[contact:referrer]] + end + + subgraph contact:permissions[ ] + style contact:permissions fill:#dd4901,stroke:white + + perm:contact:DELETE{{contact:DELETE}} + perm:contact:UPDATE{{contact:UPDATE}} + perm:contact:SELECT{{contact:SELECT}} + end +end + +%% granting roles to users +user:creator ==> role:contact:owner + +%% granting roles to roles +role:global:admin ==> role:contact:owner +role:contact:owner ==> role:contact:admin +role:contact:admin ==> role:contact:referrer + +%% granting permissions to roles +role:contact:owner ==> perm:contact:DELETE +role:contact:admin ==> perm:contact:UPDATE +role:contact:referrer ==> perm:contact:SELECT + +``` diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql new file mode 100644 index 00000000..136dad87 --- /dev/null +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql @@ -0,0 +1,126 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_contact'); +--// + + +-- ============================================================================ +--changeset hs-office-contact-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact'); +--// + + +-- ============================================================================ +--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficeContact( + NEW hs_office_contact +) + language plpgsql as $$ + +declare + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + perform createRoleWithGrants( + hsOfficeContactOwner(NEW), + permissions => array['DELETE'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + perform createRoleWithGrants( + hsOfficeContactAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsOfficeContactOwner(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeContactReferrer(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[hsOfficeContactAdmin(NEW)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_contact row. + */ + +create or replace function insertTriggerForHsOfficeContact_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficeContact(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficeContact_tg + after insert on hs_office_contact + for each row +execute procedure insertTriggerForHsOfficeContact_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-contact-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_contact, + where only global-admin has that permission. +*/ +create or replace function hs_office_contact_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_contact_insert_permission_check_tg + before insert on hs_office_contact + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_contact_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +call generateRbacIdentityViewFromProjection('hs_office_contact', + $idName$ + label + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_contact', + $orderBy$ + label + $orderBy$, + $updates$ + label = new.label, + postalAddress = new.postalAddress, + emailAddresses = new.emailAddresses, + phoneNumbers = new.phoneNumbers + $updates$); +--// + diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md new file mode 100644 index 00000000..aa971642 --- /dev/null +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md @@ -0,0 +1,43 @@ +### rbac person + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph person["`**person**`"] + direction TB + style person fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph person:roles[ ] + style person:roles fill:#dd4901,stroke:white + + role:person:owner[[person:owner]] + role:person:admin[[person:admin]] + role:person:referrer[[person:referrer]] + end + + subgraph person:permissions[ ] + style person:permissions fill:#dd4901,stroke:white + + perm:person:DELETE{{person:DELETE}} + perm:person:UPDATE{{person:UPDATE}} + perm:person:SELECT{{person:SELECT}} + end +end + +%% granting roles to users +user:creator ==> role:person:owner + +%% granting roles to roles +role:global:admin ==> role:person:owner +role:person:owner ==> role:person:admin +role:person:admin ==> role:person:referrer + +%% granting permissions to roles +role:person:owner ==> perm:person:DELETE +role:person:admin ==> perm:person:UPDATE +role:person:referrer ==> perm:person:SELECT + +``` diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql new file mode 100644 index 00000000..f99c2a46 --- /dev/null +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql @@ -0,0 +1,126 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_person'); +--// + + +-- ============================================================================ +--changeset hs-office-person-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person'); +--// + + +-- ============================================================================ +--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +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'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + 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 + call buildRbacSystemForHsOfficePerson(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficePerson_tg + after insert on hs_office_person + for each row +execute procedure insertTriggerForHsOfficePerson_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_person, + where only global-admin has that permission. +*/ +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; $$; + +create trigger hs_office_person_insert_permission_check_tg + before insert on hs_office_person + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_person_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +call generateRbacIdentityViewFromProjection('hs_office_person', + $idName$ + concat(tradeName, familyName, givenName) + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_person', + $orderBy$ + concat(tradeName, familyName, givenName) + $orderBy$, + $updates$ + personType = new.personType, + tradeName = new.tradeName, + givenName = new.givenName, + familyName = new.familyName + $updates$); +--// + diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md new file mode 100644 index 00000000..14f797eb --- /dev/null +++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md @@ -0,0 +1,100 @@ +### rbac relation + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph holderPerson["`**holderPerson**`"] + direction TB + style holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph holderPerson:roles[ ] + style holderPerson:roles fill:#99bcdb,stroke:white + + role:holderPerson:owner[[holderPerson:owner]] + role:holderPerson:admin[[holderPerson:admin]] + role:holderPerson:referrer[[holderPerson:referrer]] + end +end + +subgraph anchorPerson["`**anchorPerson**`"] + direction TB + style anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph anchorPerson:roles[ ] + style anchorPerson:roles fill:#99bcdb,stroke:white + + role:anchorPerson:owner[[anchorPerson:owner]] + role:anchorPerson:admin[[anchorPerson:admin]] + role:anchorPerson:referrer[[anchorPerson:referrer]] + end +end + +subgraph contact["`**contact**`"] + direction TB + style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph contact:roles[ ] + style contact:roles fill:#99bcdb,stroke:white + + role:contact:owner[[contact:owner]] + role:contact:admin[[contact:admin]] + role:contact:referrer[[contact:referrer]] + end +end + +subgraph relation["`**relation**`"] + direction TB + style relation fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph relation:roles[ ] + style relation:roles fill:#dd4901,stroke:white + + role:relation:owner[[relation:owner]] + role:relation:admin[[relation:admin]] + role:relation:agent[[relation:agent]] + role:relation:tenant[[relation:tenant]] + end + + subgraph relation:permissions[ ] + style relation:permissions fill:#dd4901,stroke:white + + perm:relation:DELETE{{relation:DELETE}} + perm:relation:UPDATE{{relation:UPDATE}} + perm:relation:SELECT{{relation:SELECT}} + end +end + +%% granting roles to users +user:creator ==> role:relation:owner + +%% granting roles to roles +role:global:admin -.-> role:anchorPerson:owner +role:anchorPerson:owner -.-> role:anchorPerson:admin +role:anchorPerson:admin -.-> role:anchorPerson:referrer +role:global:admin -.-> role:holderPerson:owner +role:holderPerson:owner -.-> role:holderPerson:admin +role:holderPerson:admin -.-> role:holderPerson:referrer +role:global:admin -.-> role:contact:owner +role:contact:owner -.-> role:contact:admin +role:contact:admin -.-> role:contact:referrer +role:global:admin ==> role:relation:owner +role:relation:owner ==> role:relation:admin +role:anchorPerson:admin ==> role:relation:admin +role:relation:admin ==> role:relation:agent +role:holderPerson:admin ==> role:relation:agent +role:relation:agent ==> role:relation:tenant +role:holderPerson:admin ==> role:relation:tenant +role:contact:admin ==> role:relation:tenant +role:relation:tenant ==> role:anchorPerson:referrer +role:relation:tenant ==> role:holderPerson:referrer +role:relation:tenant ==> role:contact:referrer + +%% granting permissions to roles +role:relation:owner ==> perm:relation:DELETE +role:relation:admin ==> perm:relation:UPDATE +role:relation:tenant ==> perm:relation:SELECT + +``` diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql new file mode 100644 index 00000000..5301dc56 --- /dev/null +++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql @@ -0,0 +1,191 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_relation'); +--// + + +-- ============================================================================ +--changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation'); +--// + + +-- ============================================================================ +--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficeRelation( + NEW hs_office_relation +) + language plpgsql as $$ + +declare + newHolderPerson hs_office_person; + newAnchorPerson hs_office_person; + newContact hs_office_contact; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid INTO newHolderPerson; + + SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson; + + SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact; + + perform createRoleWithGrants( + hsOfficeRelationOwner(NEW), + permissions => array['DELETE'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + perform createRoleWithGrants( + hsOfficeRelationAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[ + hsOfficePersonAdmin(newAnchorPerson), + hsOfficeRelationOwner(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeRelationAgent(NEW), + incomingSuperRoles => array[ + hsOfficePersonAdmin(newHolderPerson), + hsOfficeRelationAdmin(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeRelationTenant(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[ + hsOfficeContactAdmin(newContact), + hsOfficePersonAdmin(newHolderPerson), + hsOfficeRelationAgent(NEW)], + outgoingSubRoles => array[ + hsOfficeContactReferrer(newContact), + hsOfficePersonReferrer(newAnchorPerson), + hsOfficePersonReferrer(newHolderPerson)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row. + */ + +create or replace function insertTriggerForHsOfficeRelation_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficeRelation(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficeRelation_tg + after insert on hs_office_relation + for each row +execute procedure insertTriggerForHsOfficeRelation_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Called from the AFTER UPDATE TRIGGER to re-wire the grants. + */ + +create or replace procedure updateRbacRulesForHsOfficeRelation( + OLD hs_office_relation, + NEW hs_office_relation +) + language plpgsql as $$ +begin + + if NEW.contactUuid is distinct from OLD.contactUuid then + delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid; + call buildRbacSystemForHsOfficeRelation(NEW); + end if; +end; $$; + +/* + AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_relation row. + */ + +create or replace function updateTriggerForHsOfficeRelation_tf() + returns trigger + language plpgsql + strict as $$ +begin + call updateRbacRulesForHsOfficeRelation(OLD, NEW); + return NEW; +end; $$; + +create trigger updateTriggerForHsOfficeRelation_tg + after update on hs_office_relation + for each row +execute procedure updateTriggerForHsOfficeRelation_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-relation-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_relation, + where only global-admin has that permission. +*/ +create or replace function hs_office_relation_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_relation not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_relation_insert_permission_check_tg + before insert on hs_office_relation + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_relation_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +call generateRbacIdentityViewFromProjection('hs_office_relation', + $idName$ + (select idName from hs_office_person_iv p where p.uuid = anchorUuid) + || '-with-' || target.type || '-' + || (select idName from hs_office_person_iv p where p.uuid = holderUuid) + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_relation', + $orderBy$ + (select idName from hs_office_person_iv p where p.uuid = target.holderUuid) + $orderBy$, + $updates$ + contactUuid = new.contactUuid + $updates$); +--// + diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md new file mode 100644 index 00000000..98bd276d --- /dev/null +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md @@ -0,0 +1,158 @@ +### rbac partner + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph partnerRel.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + 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 partner["`**partner**`"] + direction TB + style partner fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph partner:permissions[ ] + style partner:permissions fill:#dd4901,stroke:white + + perm:partner:INSERT{{partner:INSERT}} + perm:partner:DELETE{{partner:DELETE}} + perm:partner:UPDATE{{partner:UPDATE}} + perm:partner:SELECT{{partner:SELECT}} + end + + subgraph partnerRel["`**partnerRel**`"] + direction TB + 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 + + 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:INSERT +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-generated.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql new file mode 100644 index 00000000..8b12e95f --- /dev/null +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql @@ -0,0 +1,248 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-partner-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_partner'); +--// + + +-- ============================================================================ +--changeset hs-office-partner-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficePartner', 'hs_office_partner'); +--// + + +-- ============================================================================ +--changeset hs-office-partner-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficePartner( + NEW hs_office_partner +) + language plpgsql as $$ + +declare + newPartnerRel hs_office_relation; + newPartnerDetails hs_office_partner_details; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); + + SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); + + call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel)); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner row. + */ + +create or replace function insertTriggerForHsOfficePartner_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficePartner(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficePartner_tg + after insert on hs_office_partner + for each row +execute procedure insertTriggerForHsOfficePartner_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-partner-rbac-update-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Called from the AFTER UPDATE TRIGGER to re-wire the grants. + */ + +create or replace procedure updateRbacRulesForHsOfficePartner( + OLD hs_office_partner, + NEW hs_office_partner +) + language plpgsql as $$ + +declare + oldPartnerRel hs_office_relation; + newPartnerRel hs_office_relation; + oldPartnerDetails hs_office_partner_details; + newPartnerDetails hs_office_partner_details; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_office_relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel; + assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid); + + SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); + + SELECT * FROM hs_office_partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails; + assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid); + + SELECT * FROM hs_office_partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails; + assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid); + + + if NEW.partnerRelUuid <> OLD.partnerRelUuid then + + call revokePermissionFromRole(getPermissionId(OLD.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel)); + + call revokePermissionFromRole(getPermissionId(OLD.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel)); + + call revokePermissionFromRole(getPermissionId(OLD.uuid, 'SELECT'), hsOfficeRelationTenant(oldPartnerRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newPartnerRel)); + + call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(oldPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'DELETE'), hsOfficeRelationAdmin(newPartnerRel)); + + call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(oldPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'UPDATE'), hsOfficeRelationAgent(newPartnerRel)); + + call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(oldPartnerRel)); + call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAgent(newPartnerRel)); + + end if; + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_partner row. + */ + +create or replace function updateTriggerForHsOfficePartner_tf() + returns trigger + language plpgsql + strict as $$ +begin + call updateRbacRulesForHsOfficePartner(OLD, NEW); + return NEW; +end; $$; + +create trigger updateTriggerForHsOfficePartner_tg + after update on hs_office_partner + for each row +execute procedure updateTriggerForHsOfficePartner_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-partner-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates INSERT INTO hs_office_partner permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalAdmin()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_partner INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_partner_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + createPermission(NEW.uuid, 'INSERT', 'hs_office_partner'), + globalAdmin()); + return NEW; +end; $$; + +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist +create trigger z_hs_office_partner_global_insert_tg + after insert on global + for each row +execute procedure hs_office_partner_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_partner, + where only global-admin has that permission. +*/ +create or replace function hs_office_partner_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_partner not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_partner_insert_permission_check_tg + before insert on hs_office_partner + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_partner_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + + call generateRbacIdentityViewFromQuery('hs_office_partner', + $idName$ + SELECT partner.partnerNumber + || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid) + || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid) + FROM hs_office_partner AS partner + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_partner', + $orderBy$ + SELECT partner.partnerNumber + || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid) + || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid) + FROM hs_office_partner AS partner + $orderBy$, + $updates$ + partnerRelUuid = new.partnerRelUuid, + personUuid = new.personUuid, + contactUuid = new.contactUuid + $updates$); +--// + diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md new file mode 100644 index 00000000..ece32f9c --- /dev/null +++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md @@ -0,0 +1,136 @@ +### rbac partnerDetails + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph partnerRel.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + 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 partnerDetails["`**partnerDetails**`"] + direction TB + style partnerDetails fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph partnerDetails:permissions[ ] + style partnerDetails:permissions fill:#dd4901,stroke:white + + perm:partnerDetails:INSERT{{partnerDetails:INSERT}} + end + + subgraph partnerRel["`**partnerRel**`"] + direction TB + 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 + + 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 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:partnerDetails:INSERT + +``` diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql new file mode 100644 index 00000000..4fd78a87 --- /dev/null +++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql @@ -0,0 +1,164 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_partner_details'); +--// + + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficePartnerDetails', 'hs_office_partner_details'); +--// + + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficePartnerDetails( + NEW hs_office_partner_details +) + language plpgsql as $$ + +declare + newPartnerRel hs_office_relation; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT partnerRel.* + FROM hs_office_relation AS partnerRel + JOIN hs_office_partner AS partner + ON partner.detailsUuid = NEW.uuid + WHERE partnerRel.uuid = partner.partnerRelUuid + INTO newPartnerRel; + assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner_details row. + */ + +create or replace function insertTriggerForHsOfficePartnerDetails_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficePartnerDetails(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficePartnerDetails_tg + after insert on hs_office_partner_details + for each row +execute procedure insertTriggerForHsOfficePartnerDetails_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates INSERT INTO hs_office_partner_details permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalAdmin()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner_details'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_partner_details INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_partner_details_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + createPermission(NEW.uuid, 'INSERT', 'hs_office_partner_details'), + globalAdmin()); + return NEW; +end; $$; + +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist +create trigger z_hs_office_partner_details_global_insert_tg + after insert on global + for each row +execute procedure hs_office_partner_details_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_partner_details, + where only global-admin has that permission. +*/ +create or replace function hs_office_partner_details_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_partner_details not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_partner_details_insert_permission_check_tg + before insert on hs_office_partner_details + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_partner_details_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + + call generateRbacIdentityViewFromQuery('hs_office_partner_details', + $idName$ + SELECT partner_iv.idName || '-details' + FROM hs_office_partner_details AS partnerDetails + JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid + JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-partner-details-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_partner_details', + $orderBy$ + SELECT partner_iv.idName || '-details' + FROM hs_office_partner_details AS partnerDetails + JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid + JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid + $orderBy$, + $updates$ + registrationOffice = new.registrationOffice, + registrationNumber = new.registrationNumber, + birthPlace = new.birthPlace, + birthName = new.birthName, + birthday = new.birthday, + dateOfDeath = new.dateOfDeath + $updates$); +--// + diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md new file mode 100644 index 00000000..4f1604fb --- /dev/null +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md @@ -0,0 +1,43 @@ +### rbac bankAccount + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph bankAccount["`**bankAccount**`"] + direction TB + style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph bankAccount:roles[ ] + style bankAccount:roles fill:#dd4901,stroke:white + + role:bankAccount:owner[[bankAccount:owner]] + role:bankAccount:admin[[bankAccount:admin]] + role:bankAccount:referrer[[bankAccount:referrer]] + end + + subgraph bankAccount:permissions[ ] + style bankAccount:permissions fill:#dd4901,stroke:white + + 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: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-generated.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql new file mode 100644 index 00000000..6b96fb34 --- /dev/null +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql @@ -0,0 +1,125 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_bankaccount'); +--// + + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount'); +--// + + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +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'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + 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 + call buildRbacSystemForHsOfficeBankAccount(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficeBankAccount_tg + after insert on hs_office_bankaccount + for each row +execute procedure insertTriggerForHsOfficeBankAccount_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount, + where only global-admin has that permission. +*/ +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; $$; + +create trigger hs_office_bankaccount_insert_permission_check_tg + before insert on hs_office_bankaccount + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_bankaccount_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +call generateRbacIdentityViewFromProjection('hs_office_bankaccount', + $idName$ + iban || ':' || holder + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_bankaccount', + $orderBy$ + iban || ':' || holder + $orderBy$, + $updates$ + holder = new.holder, + iban = new.iban, + bic = new.bic + $updates$); +--// + diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md new file mode 100644 index 00000000..f542e78c --- /dev/null +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md @@ -0,0 +1,178 @@ +### rbac sepaMandate + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph bankAccount["`**bankAccount**`"] + direction TB + 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 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 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-generated.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql new file mode 100644 index 00000000..1e383951 --- /dev/null +++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql @@ -0,0 +1,143 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_sepamandate'); +--// + + +-- ============================================================================ +--changeset hs-office-sepamandate-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate'); +--// + + +-- ============================================================================ +--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficeSepaMandate( + NEW hs_office_sepamandate +) + language plpgsql as $$ + +declare + newBankAccount hs_office_bankaccount; + newDebitorRel hs_office_relation; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount; + + SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; + + perform createRoleWithGrants( + hsOfficeSepaMandateOwner(NEW), + permissions => array['DELETE'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + perform createRoleWithGrants( + hsOfficeSepaMandateAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeSepaMandateAgent(NEW), + incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)], + outgoingSubRoles => array[ + hsOfficeBankAccountReferrer(newBankAccount), + hsOfficeRelationAgent(newDebitorRel)] + ); + + perform createRoleWithGrants( + hsOfficeSepaMandateReferrer(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[ + hsOfficeBankAccountAdmin(newBankAccount), + hsOfficeRelationAgent(newDebitorRel), + hsOfficeSepaMandateAgent(NEW)], + outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row. + */ + +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 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, + where only global-admin has that permission. +*/ +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 + 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', + $idName$ + concat(tradeName, familyName, givenName) + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_sepamandate', + $orderBy$ + concat(tradeName, familyName, givenName) + $orderBy$, + $updates$ + reference = new.reference, + agreement = new.agreement, + validity = new.validity + $updates$); +--// + diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md new file mode 100644 index 00000000..a1baa702 --- /dev/null +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md @@ -0,0 +1,275 @@ +### rbac debitor + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +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 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 debitor["`**debitor**`"] + direction TB + style debitor fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph debitor:permissions[ ] + style debitor:permissions fill:#dd4901,stroke:white + + perm:debitor:INSERT{{debitor:INSERT}} + perm:debitor:DELETE{{debitor:DELETE}} + perm:debitor:UPDATE{{debitor:UPDATE}} + perm:debitor:SELECT{{debitor:SELECT}} + end + + subgraph debitorRel["`**debitorRel**`"] + direction TB + style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + 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.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: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 +end + +subgraph partnerRel["`**partnerRel**`"] + direction TB + style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + 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.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + 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: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 + +subgraph partnerRel.contact["`**partnerRel.contact**`"] + direction TB + style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + 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 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 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 refundBankAccount["`**refundBankAccount**`"] + direction TB + style refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph refundBankAccount:roles[ ] + style refundBankAccount:roles fill:#99bcdb,stroke:white + + role:refundBankAccount:owner[[refundBankAccount:owner]] + role:refundBankAccount:admin[[refundBankAccount:admin]] + role:refundBankAccount:referrer[[refundBankAccount:referrer]] + end +end + +%% 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:refundBankAccount:owner +role:refundBankAccount:owner -.-> role:refundBankAccount:admin +role:refundBankAccount:admin -.-> role:refundBankAccount:referrer +role:refundBankAccount:admin ==> role:debitorRel:agent +role:debitorRel:agent ==> role:refundBankAccount:referrer +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 +role:partnerRel:admin ==> role:debitorRel:admin +role:partnerRel:agent ==> role:debitorRel:agent +role:debitorRel:agent ==> role:partnerRel:tenant + +%% granting permissions to roles +role:global:admin ==> perm:debitor:INSERT +role:debitorRel:owner ==> perm:debitor:DELETE +role:debitorRel:admin ==> perm:debitor:UPDATE +role:debitorRel:tenant ==> perm:debitor:SELECT + +``` diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql new file mode 100644 index 00000000..f827ea67 --- /dev/null +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql @@ -0,0 +1,231 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_office_debitor'); +--// + + +-- ============================================================================ +--changeset hs-office-debitor-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor'); +--// + + +-- ============================================================================ +--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsOfficeDebitor( + NEW hs_office_debitor +) + language plpgsql as $$ + +declare + newPartnerRel hs_office_relation; + newDebitorRel hs_office_relation; + newRefundBankAccount hs_office_bankaccount; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel; + + SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel; + assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); + + SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount; + + call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationAgent(newDebitorRel)); + call grantRoleToRole(hsOfficeRelationAdmin(newDebitorRel), hsOfficeRelationAdmin(newPartnerRel)); + call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount)); + call grantRoleToRole(hsOfficeRelationAgent(newDebitorRel), hsOfficeRelationAgent(newPartnerRel)); + call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficeRelationAgent(newDebitorRel)); + + call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationOwner(newDebitorRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationTenant(newDebitorRel)); + call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationAdmin(newDebitorRel)); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row. + */ + +create or replace function insertTriggerForHsOfficeDebitor_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsOfficeDebitor(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsOfficeDebitor_tg + after insert on hs_office_debitor + for each row +execute procedure insertTriggerForHsOfficeDebitor_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-debitor-rbac-update-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Called from the AFTER UPDATE TRIGGER to re-wire the grants. + */ + +create or replace procedure updateRbacRulesForHsOfficeDebitor( + OLD hs_office_debitor, + NEW hs_office_debitor +) + language plpgsql as $$ +begin + + if NEW.refundBankAccountUuid is distinct from OLD.refundBankAccountUuid then + delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid; + call buildRbacSystemForHsOfficeDebitor(NEW); + end if; +end; $$; + +/* + AFTER INSERT TRIGGER to re-wire the grant structure for a new hs_office_debitor row. + */ + +create or replace function updateTriggerForHsOfficeDebitor_tf() + returns trigger + language plpgsql + strict as $$ +begin + call updateRbacRulesForHsOfficeDebitor(OLD, NEW); + return NEW; +end; $$; + +create trigger updateTriggerForHsOfficeDebitor_tg + after update on hs_office_debitor + for each row +execute procedure updateTriggerForHsOfficeDebitor_tf(); +--// + + +-- ============================================================================ +--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates INSERT INTO hs_office_debitor permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalAdmin()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_debitor INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_debitor_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'), + globalAdmin()); + return NEW; +end; $$; + +-- z_... is to put it at the end of after insert triggers, to make sure the roles exist +create trigger z_hs_office_debitor_global_insert_tg + after insert on global + for each row +execute procedure hs_office_debitor_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_debitor, + where only global-admin has that permission. +*/ +create or replace function hs_office_debitor_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_debitor not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_office_debitor_insert_permission_check_tg + before insert on hs_office_debitor + for each row + when ( not isGlobalAdmin() ) + execute procedure hs_office_debitor_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + + call generateRbacIdentityViewFromQuery('hs_office_debitor', + $idName$ + SELECT debitor.uuid, + 'D-' || (SELECT partner.partnerNumber + FROM hs_office_partner partner + JOIN hs_office_relation partnerRel + ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER' + JOIN hs_office_relation debitorRel + ON debitorRel.anchorUuid = partnerRel.holderUuid AND partnerRel.type = 'DEBITOR' + WHERE debitorRel.uuid = debitor.debitorRelUuid) + || to_char(debitorNumberSuffix, 'fm00') + from hs_office_debitor as debitor + $idName$); +--// + +-- ============================================================================ +--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_office_debitor', + $orderBy$ + SELECT debitor.uuid, + 'D-' || (SELECT partner.partnerNumber + FROM hs_office_partner partner + JOIN hs_office_relation partnerRel + ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER' + JOIN hs_office_relation debitorRel + ON debitorRel.anchorUuid = partnerRel.holderUuid AND partnerRel.type = 'DEBITOR' + WHERE debitorRel.uuid = debitor.debitorRelUuid) + || to_char(debitorNumberSuffix, 'fm00') + from hs_office_debitor as debitor + $orderBy$, + $updates$ + debitorRel = new.debitorRel, + billable = new.billable, + debitorUuid = new.debitorUuid, + refundBankAccountUuid = new.refundBankAccountUuid, + vatId = new.vatId, + vatCountryCode = new.vatCountryCode, + vatBusiness = new.vatBusiness, + vatReverseCharge = new.vatReverseCharge, + defaultPrefix = new.defaultPrefix + $updates$); +--// + diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java index cf3b18bf..78d64e6a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java @@ -137,12 +137,14 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean .body(""" { "type": "%s", + "mark": "%s", "anchorUuid": "%s", "holderUuid": "%s", "contactUuid": "%s" } """.formatted( - HsOfficeRelationTypeResource.DEBITOR, + HsOfficeRelationTypeResource.SUBSCRIBER, + "operations-discuss", givenAnchorPerson.getUuid(), givenHolderPerson.getUuid(), givenContact.getUuid())) @@ -153,7 +155,8 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean .statusCode(201) .contentType(ContentType.JSON) .body("uuid", isUuidValid()) - .body("type", is("DEBITOR")) + .body("type", is("SUBSCRIBER")) + .body("mark", is("operations-discuss")) .body("anchor.tradeName", is("Third OHG")) .body("holder.givenName", is("Paul")) .body("contact.label", is("second contact")) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java index 4555e2e3..287b3f4b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java @@ -79,7 +79,8 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea final var newRelation = HsOfficeRelationEntity.builder() .anchor(givenAnchorPerson) .holder(givenHolderPerson) - .type(HsOfficeRelationType.REPRESENTATIVE) + .type(HsOfficeRelationType.SUBSCRIBER) + .mark("operations-announce") .contact(givenContact) .build(); return toCleanup(relationRepo.save(newRelation)); @@ -90,6 +91,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationEntity::getUuid).isNotNull(); assertThatRelationIsPersisted(result.returnedValue()); assertThat(relationRepo.count()).isEqualTo(count + 1); + final var stored = relationRepo.findByUuid(result.returnedValue().getUuid()); + assertThat(stored).isNotEmpty().map(HsOfficeRelationEntity::toString).get() + .isEqualTo("rel(anchor='NP Bessler, Anita', type='SUBSCRIBER', mark='operations-announce', holder='NP Bessler, Anita', contact='fourth contact')"); } @Test