From 6a3999159209737529f33e86bd49c560d3ec74d0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 6 Feb 2024 16:19:33 +0100 Subject: [PATCH] apply cookie cutter pattern to relationship --- .../223-hs-office-relationship-rbac.md | 215 ++++-------------- .../223-hs-office-relationship-rbac.sql | 70 +++--- 2 files changed, 82 insertions(+), 203 deletions(-) diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md index c41de32c..ab91889c 100644 --- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md +++ b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md @@ -10,183 +10,62 @@ subgraph global role:global.admin[global.admin] end -subgraph hsOfficeContact +subgraph anchorPerson direction TB - style hsOfficeContact fill:#eee + style anchorPerson fill:#eee - role:hsOfficeContact.admin[contact.admin] - --> role:hsOfficeContact.tenant[contact.tenant] - --> role:hsOfficeContact.guest[contact.guest] + role:anchorPerson.owner[anchorPerson.owner] + --> role:anchorPerson.admin[anchorPerson.admin] + --> role:anchorPerson.referrer[anchorPerson.referrer] end -subgraph hsOfficePerson +subgraph holderPerson direction TB - style hsOfficePerson fill:#eee + style holderPerson fill:#eee - role:hsOfficePerson.admin[person.admin] - --> role:hsOfficePerson.tenant[person.tenant] - --> role:hsOfficePerson.guest[person.guest] + role:holderPerson.owner[holderPerson.owner] + --> role:holderPerson.admin[holderPerson.admin] + --> role:holderPerson.referrer[holderPerson.referrer] end -subgraph hsOfficeRelationship +subgraph contact + direction TB + style contact fill:#eee + + role:contact.owner[contact.admin] + --> role:contact.admin[contact.admin] + --> role:contact.referrer[contact.referrer] +end - role:hsOfficePerson#relAnchor.admin[person#anchor.admin] - --- role:hsOfficePerson.admin +subgraph relationship + + role:relationship.owner[relationship.owner] + %% permissions + role:relationship.owner --> perm:relationship.*{{relationship.*}} + %% incoming + role:global.admin ---> role:relationship.owner - role:hsOfficeRelationship.owner[relationship.owner] - %% permissions - role:hsOfficeRelationship.owner --> perm:hsOfficeRelationship.*{{relationship.*}} - %% incoming - role:global.admin ---> role:hsOfficeRelationship.owner - role:hsOfficePersonAdmin#relAnchor.admin + role:relationship.admin[relationship.admin] + %% permissions + role:relationship.admin --> perm:relationship.edit{{relationship.edit}} + %% incoming + role:relationship.owner --> role:relationship.admin + role:anchorPerson.admin --> role:relationship.admin + + role:relationship.agent[relationship.agent] + %% incoming + role:relationship.admin --> role:relationship.agent + role:holderPerson.admin --> role:relationship.agent + role:contact.admin --> role:relationship.agent + + role:relationship.tenant[relationship.tenant] + %% permissions + role:relationship.tenant --> perm:relationship.view{{relationship.view}} + %% incoming + role:relationship.agent --> role:relationship.tenant + %% outgoing + role:relationship.tenant --> role:anchorPerson.referrer + role:relationship.tenant --> role:holderPerson.referrer + role:relationship.tenant --> role:contact.referrer end ``` - - if TG_OP = 'INSERT' then - - -- the owner role with full access for admins of the relAnchor global admins - ownerRole = createRole( - hsOfficeRelationshipOwner(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), - beneathRoles(array[ - globalAdmin(), - hsOfficePersonAdmin(newRelAnchor)]) - ); - - -- the admin role with full access for the owner - adminRole = createRole( - hsOfficeRelationshipAdmin(NEW), - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), - beneathRole(ownerRole) - ); - - -- the tenant role for those related users who can view the data - perform createRole( - hsOfficeRelationshipTenant, - grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), - beneathRoles(array[ - hsOfficePersonAdmin(newRelAnchor), - hsOfficePersonAdmin(newRelHolder), - hsOfficeContactAdmin(newContact)]), - withSubRoles(array[ - hsOfficePersonTenant(newRelAnchor), - hsOfficePersonTenant(newRelHolder), - hsOfficeContactTenant(newContact)]) - ); - - -- anchor and holder admin roles need each others tenant role - -- to be able to see the joined relationship - call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder)); - call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor)); - call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact)); - - elsif TG_OP = 'UPDATE' then - - if OLD.contactUuid <> NEW.contactUuid then - -- nothing but 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( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant ); - call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant ); - end if; - else - raise exception 'invalid usage of TRIGGER'; - end if; - - return NEW; -end; $$; - -/* - An AFTER INSERT TRIGGER which creates the role structure for a new customer. - */ -create trigger createRbacRolesForHsOfficeRelationship_Trigger - after insert - on hs_office_relationship - for each row -execute procedure hsOfficeRelationshipRbacRolesTrigger(); - -/* - An AFTER UPDATE TRIGGER which updates the role structure of a customer. - */ -create trigger updateRbacRolesForHsOfficeRelationship_Trigger - after update - on hs_office_relationship - for each row -execute procedure hsOfficeRelationshipRbacRolesTrigger(); ---// - - --- ============================================================================ ---changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -call generateRbacIdentityView('hs_office_relationship', $idName$ - (select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid) - || '-with-' || target.relType || '-' || - (select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid) - $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$ - contactUuid = new.contactUuid - $updates$); ---// - --- TODO: exception if one tries to amend any other column - - --- ============================================================================ ---changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -/* - Creates a global permission for new-relationship and assigns it to the hostsharing admins role. - */ -do language plpgsql $$ - declare - addCustomerPermissions uuid[]; - globalObjectUuid uuid; - globalAdminRoleUuid uuid ; - begin - call defineContext('granting global new-relationship permission to global admin role', null, null, null); - - globalAdminRoleUuid := findRoleId(globalAdmin()); - globalObjectUuid := (select uuid from global); - addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']); - call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions); - end; -$$; - -/** - Used by the trigger to prevent the add-customer to current user respectively assumed roles. - */ -create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects() - returns trigger - language PLPGSQL -as $$ -begin - raise exception '[403] new-relationship 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_relationship_insert_trigger - before insert - on hs_office_relationship - for each row - -- TODO.spec: who is allowed to create new relationships - when ( not hasAssumedRole() ) -execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects(); ---// - 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 cd4cb48a..ddf0080b 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,72 +27,72 @@ 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 - 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['*'], incomingSuperRoles => array[ - globalAdmin(), - hsOfficePersonAdmin(newRelAnchor)] - ); + globalAdmin() + ] + ); perform createRoleWithGrants( hsOfficeRelationshipAdmin(NEW), permissions => array['edit'], - 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['view'], + 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['view'], + incomingSuperRoles => array[ + hsOfficeRelationshipAdmin(NEW) + ], + outgoingSubRoles => array[ +-- hsOfficePersonAdmin(newAnchorPerson), +-- hsOfficePersonAdmin(newHolderPerson), + hsOfficeContactAdmin(newContact) + ] + ); 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( hsOfficeRelationshipTenant(NEW), hsOfficeContactAdmin(oldContact) ); + call grantRoleToRole( hsOfficeRelationshipTenant(NEW), hsOfficeContactAdmin(newContact) ); - call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant ); - call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant ); + call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipAgent(NEW) ); + call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipAgent(NEW) ); end if; else raise exception 'invalid usage of TRIGGER';