diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index feedf419..80a74a20 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -397,6 +397,7 @@ select exists( ); $$; +-- TODO: the array parameter and thus the array return value is only used in toPermissionUuids, simplify to non-arrays create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[]) returns uuid[] language plpgsql as $$ @@ -668,6 +669,26 @@ begin end if; end; $$; +create or replace procedure revokePermissionFromRole(permission RbacRoleDescriptor, superRole RbacRoleDescriptor) + language plpgsql as $$ +declare + superRoleId uuid; + subRoleId uuid; +begin + superRoleId := findRoleId(superRole); + subRoleId := findRoleId(subRole); + + perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole'); + perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole'); + + if (isGranted(superRoleId, subRoleId)) then + delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId; + else + raise exception 'cannot revoke role % (%) from % (% because it is not granted', + subRole, subRoleId, superRole, superRoleId; + end if; +end; $$; + -- ============================================================================ --changeset rbac-base-QUERY-ACCESSIBLE-OBJECT-UUIDS:1 endDelimiter:--// -- ---------------------------------------------------------------------------- diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql index fa198308..27ee5f56 100644 --- a/src/main/resources/db/changelog/058-rbac-generators.sql +++ b/src/main/resources/db/changelog/058-rbac-generators.sql @@ -74,6 +74,7 @@ begin return roleDescriptor('%2$s', entity.uuid, 'tenant'); end; $f$; + -- TODO: remove guest role create or replace function %1$sGuest(entity %2$s) returns RbacRoleDescriptor language plpgsql @@ -82,6 +83,14 @@ begin return roleDescriptor('%2$s', entity.uuid, 'guest'); end; $f$; + create or replace function %1$sReferrer(entity %2$s) + returns RbacRoleDescriptor + language plpgsql + strict as $f$ + begin + return roleDescriptor('%2$s', entity.uuid, 'referrer'); + end; $f$; + $sql$, prefix, targetTable); execute sql; end; $$; diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql index 3aa82dc4..9ff94746 100644 --- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql @@ -7,13 +7,6 @@ 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-ROLES-CREATION:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -35,70 +28,40 @@ begin if TG_OP = 'INSERT' then - -- === ATTENTION: code generated from related Mermaid flowchart: === - - perform createRoleWithGrants( - hsOfficePartnerOwner(NEW), - permissions => array['*'], - incomingSuperRoles => array[globalAdmin()], - outgoingSubRoles => array[ - hsOfficeRelationshipOwner(newPartnerRole)] - ); - - perform createRoleWithGrants( - hsOfficePartnerAdmin(NEW), - permissions => array['edit'], - incomingSuperRoles => array[ - hsOfficePartnerOwner(NEW)], - outgoingSubRoles => array[ - hsOfficeRelationshipAdmin(newPartnerRole)] - ); - - perform createRoleWithGrants( - hsOfficePartnerAgent(NEW), - incomingSuperRoles => array[ - hsOfficePartnerAdmin(NEW), - hsOfficeRelationshipAgent(newPartnerRole)] - ); - - perform createRoleWithGrants( - hsOfficePartnerTenant(NEW), - incomingSuperRoles => array[ - hsOfficePartnerAgent(NEW)], - outgoingSubRoles => array[ - hsOfficeRelationshipTenant(newPartnerRole)] - ); - - perform createRoleWithGrants( - hsOfficePartnerGuest(NEW), - permissions => array['view'], - incomingSuperRoles => array[hsOfficePartnerTenant(NEW)] - ); - - -- === END of code generated from Mermaid flowchart. === - - -- Each partner-details entity belong exactly to one partner entity - -- and it makes little sense just to delegate partner-details roles. - -- Therefore, we did not model partner-details roles, - -- but instead just assign extra permissions to existing partner-roles. - - --Attention: Cannot be in partner-details because of insert order (partner is not in database yet) + -- Permissions and Grants for Partner call grantPermissionsToRole( - getRoleId(hsOfficePartnerOwner(NEW), 'fail'), + getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), + createPermissions(NEW.uuid, array ['*']) + ); + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), + createPermissions(NEW.uuid, array ['edit']) + ); + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipTenant(newPartnerRole), 'fail'), + createPermissions(NEW.uuid, array ['view']) + ); + + -- Permissions and Grants for PartnerDetails + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), createPermissions(NEW.detailsUuid, array ['*']) ); call grantPermissionsToRole( - getRoleId(hsOfficePartnerAdmin(NEW), 'fail'), + getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), createPermissions(NEW.detailsUuid, array ['edit']) ); call grantPermissionsToRole( - -- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT. - -- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT! + -- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT. + -- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT! -- Otherwise package-admins etc. would be able to read the data. - getRoleId(hsOfficePartnerAgent(NEW), 'fail'), + getRoleId(hsOfficeRelationshipAgent(newPartnerRole), 'fail'), createPermissions(NEW.detailsUuid, array ['view']) ); @@ -108,14 +71,47 @@ begin if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole; - call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRole), hsOfficePartnerAdmin(OLD)); - call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRole), hsOfficePartnerAdmin(NEW)); + -- Revoke all Permissions from old partner relationship + -- TODO: introduce call revokeAllPermissionsOnDescendantFromAllRolesOfAscendant(OLD, oldPartnerRole); + delete from rbacGrants where descendantUuid==OLD.uuid and ascendantUuid==OLD.partnerRoleUuid; - call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationshipAdmin(oldPartnerRole)); - call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationshipAdmin(newPartnerRole)); + -- Grants for Partner + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), + array[findPermissionId(NEW.uuid, '*')] + ); + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), + array[findPermissionId(NEW.uuid, 'edit')] + ); + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipTenant(newPartnerRole), 'fail'), + array[findPermissionId(NEW.uuid, 'view')] + ); + + -- Grants for PartnerDetails + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipOwner(newPartnerRole), 'fail'), + array[findPermissionId(NEW.detailsUuid, '*')] + ); + + call grantPermissionsToRole( + getRoleId(hsOfficeRelationshipAdmin(newPartnerRole), 'fail'), + array[findPermissionId(NEW.detailsUuid, array ['edit'])] + ); + + call grantPermissionsToRole( + -- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT. + -- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT! + -- Otherwise package-admins etc. would be able to read the data. + getRoleId(hsOfficeRelationshipAgent(newPartnerRole), 'fail'), + array[findPermissionId(NEW.detailsUuid, 'view')] + ); - call revokeRoleFromRole(hsOfficeRelationshipGuest(oldPartnerRole), hsOfficePartnerTenant(OLD)); - call grantRoleToRole(hsOfficeRelationshipGuest(newPartnerRole), hsOfficePartnerTenant(NEW)); end if; else diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md index 0a67e6c0..f0455e2f 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md @@ -3,9 +3,24 @@ ```mermaid flowchart TB -subgraph bank[ ] - style bank fill:#fff +subgraph partnerRelationship[hsOfficeRelationship:PARTNER] + direction TB + style partnerRelationship fill:#eee + role:partnerRelationship.owner[relationship.owner] + --> role:partnerRelationship.admin[relationship.admin] + --> role:partnerRelationship.agent[relationship.agent] + --> role:partnerRelationship.tenant[relationship.tenant] + + partnerPersonAdmin>e.g. partnerPerson.admin] --> role:partnerRelationship.agent + otherPersonAdmin>e.g. operationalPerson.admin] --> role:partnerRelationship.tenant + role:partnerRelationship.tenant --> partnerPersonReferrer>e.g. partnerPerson.referrer] +end + +subgraph internal[ ] + direction TB + style internal fill:#fff + subgraph refundBankAccount direction TB style refundBankAccount fill:#eee @@ -14,28 +29,7 @@ subgraph bank[ ] --> role:refundBankAccount.admin[bankAccount.admin] --> role:refundBankAccount.referrer[bankAccount.referrer] end -end -subgraph partner[ ] - style partner fill:#fff - - subgraph partnerRelationship[hsOfficeRelationship:PARTNER] - direction TB - style partnerRelationship fill:#eee - - role:partnerRelationship.owner[relationship.owner] - --> role:partnerRelationship.admin[relationship.admin] - --> role:partnerRelationship.agent[relationship.agent] - --> role:partnerRelationship.tenant[relationship.tenant] - - partnerPerson[e.g. partnerPerson.admin] --> role:partnerRelationship.agent - otherPerson[e.g. operationalPerson.admin] --> role:partnerRelationship.tenant - end -end - -subgraph internal[ ] - direction TB - style internal fill:#fff subgraph debitorRelationship[hsOfficeRelationship:DEBITOR] direction TB @@ -56,7 +50,7 @@ subgraph internal[ ] role:debitorRelationship.admin[debitorRelationship.admin] %% permissions - ==> perm:debitor.edit{{debitorRelationship.edit}} + ==> perm:debitor.edit{{debitor.edit}} %% incoming role:partnerRelationship.admin ==> role:debitorRelationship.admin %% outgoing diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index 656195ec..5acf78a3 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -30,6 +30,8 @@ declare hsOfficeDebitorTenant RbacRoleDescriptor; oldPartner hs_office_partner; newPartner hs_office_partner; + oldPartnerRel hs_office_relationship; + newPartnerRel hs_office_relationship; oldContact hs_office_contact; newContact hs_office_contact; newBankAccount hs_office_bankaccount; @@ -39,10 +41,11 @@ begin hsOfficeDebitorTenant := hsOfficeDebitorTenant(NEW); select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newPartner; + select * from hs_office_relationship as r where r.relType = 'PARTNER' and r.relHolderUuid = NEW.partnerUuid into newPartnerRel; select * from hs_office_contact as c where c.uuid = NEW.billingContactUuid into newContact; select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount; - if TG_OP = 'INSERT' then + if TG_OP = 'INSERT' then perform createRoleWithGrants( hsOfficeDebitorOwner(NEW), @@ -62,7 +65,7 @@ begin hsOfficeDebitorAgent(NEW), incomingSuperRoles => array[ hsOfficeDebitorAdmin(NEW), - hsOfficePartnerAdmin(newPartner), + hsOfficeRelationshipAdmin(newPartnerRel), hsOfficeContactAdmin(newContact)], outgoingSubRoles => array[ hsOfficeBankAccountTenant(newBankaccount)] @@ -72,10 +75,10 @@ begin hsOfficeDebitorTenant(NEW), incomingSuperRoles => array[ hsOfficeDebitorAgent(NEW), - hsOfficePartnerAgent(newPartner), + hsOfficeRelationshipAgent(newPartnerRel), hsOfficeBankAccountAdmin(newBankaccount)], outgoingSubRoles => array[ - hsOfficePartnerTenant(newPartner), + hsOfficeRelationshipTenant(newPartnerRel), hsOfficeContactGuest(newContact), hsOfficeBankAccountGuest(newBankaccount)] ); @@ -91,15 +94,16 @@ begin if OLD.partnerUuid <> NEW.partnerUuid then select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner; + select * from hs_office_relationship as r where r.uuid = OLD.partnerUuid into oldPartnerRel; - call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficePartnerAdmin(oldPartner)); - call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficePartnerAdmin(newPartner)); + call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeRelationshipAdmin(oldPartnerRel)); + call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeRelationshipAdmin(oldPartnerRel)); - call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficePartnerAgent(oldPartner)); - call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficePartnerAgent(newPartner)); + call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeRelationshipAgent(oldPartnerRel)); + call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeRelationshipAgent(newPartner)); - call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorTenant(OLD)); - call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorTenant(NEW)); + call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRel), hsOfficeDebitorTenant(OLD)); + call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeDebitorTenant(NEW)); end if; if OLD.billingContactUuid <> NEW.billingContactUuid then diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index 8197cf09..031480b2 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -27,11 +27,13 @@ create or replace function hsOfficeMembershipRbacRolesTrigger() language plpgsql strict as $$ declare - newHsOfficePartner hs_office_partner; - newHsOfficeDebitor hs_office_debitor; + newHsOfficePartner hs_office_partner; + newHsOfficePartnerRel hs_office_relationship; + newHsOfficeDebitor hs_office_debitor; begin select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newHsOfficePartner; + select * from hs_office_relationship as r where r.relType = 'PARTNER' and r.relHolderUuid = NEW.partnerUuid into newHsOfficePartnerRel; select * from hs_office_debitor as c where c.uuid = NEW.mainDebitorUuid into newHsOfficeDebitor; if TG_OP = 'INSERT' then @@ -52,20 +54,20 @@ begin perform createRoleWithGrants( hsOfficeMembershipAgent(NEW), - incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW), hsOfficePartnerAdmin(newHsOfficePartner), hsOfficeDebitorAdmin(newHsOfficeDebitor)], - outgoingSubRoles => array[hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)] + incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW), hsOfficeRelationshipAdmin(newHsOfficePartnerRel), hsOfficeDebitorAdmin(newHsOfficeDebitor)], + outgoingSubRoles => array[hsOfficeRelationshipTenant(newHsOfficePartnerRel), hsOfficeDebitorTenant(newHsOfficeDebitor)] ); perform createRoleWithGrants( hsOfficeMembershipTenant(NEW), - incomingSuperRoles => array[hsOfficeMembershipAgent(NEW), hsOfficePartnerAgent(newHsOfficePartner), hsOfficeDebitorAgent(newHsOfficeDebitor)], - outgoingSubRoles => array[hsOfficePartnerGuest(newHsOfficePartner), hsOfficeDebitorGuest(newHsOfficeDebitor)] + incomingSuperRoles => array[hsOfficeMembershipAgent(NEW), hsOfficeRelationshipAgent(newHsOfficePartnerRel), hsOfficeDebitorAgent(newHsOfficeDebitor)], + outgoingSubRoles => array[hsOfficeRelationshipGuest(newHsOfficePartnerRel), hsOfficeDebitorGuest(newHsOfficeDebitor)] ); perform createRoleWithGrants( hsOfficeMembershipGuest(NEW), permissions => array['view'], - incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)] + incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficeRelationshipTenant(newHsOfficePartnerRel), hsOfficeDebitorTenant(newHsOfficeDebitor)] ); -- === END of code generated from Mermaid flowchart. ===