structured-liquibase-files (#29)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/29 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
		| @@ -0,0 +1,23 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table if not exists hs_office_contact | ||||
| ( | ||||
|     uuid           uuid unique references RbacObject (uuid) initially deferred, | ||||
|     label          varchar(128) not null, | ||||
|     postalAddress  text, | ||||
|     emailAddresses text, -- TODO.feat: change to json | ||||
|     phoneNumbers   text  -- TODO.feat: change to json | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_contact'); | ||||
| --// | ||||
| @@ -0,0 +1,45 @@ | ||||
| ### 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}} | ||||
|         perm:contact:INSERT{{contact:INSERT}} | ||||
|     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 | ||||
| role:global:GUEST ==> perm:contact:INSERT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,146 @@ | ||||
| --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:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_contact permissions for the related global rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row global; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_contact permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_contact'), | ||||
|                     globalGUEST()); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_contact INSERT permission to specified role of new global rows. | ||||
| */ | ||||
| create or replace function hs_office_contact_global_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_contact'), | ||||
|             globalGUEST()); | ||||
|     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_contact_global_insert_tg | ||||
|     after insert on global | ||||
|     for each row | ||||
| execute procedure hs_office_contact_global_insert_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$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,96 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- TODO: These changesets are just for the external remote views to simulate the legacy tables. | ||||
| --  Once we don't need the external remote views anymore, create revert changesets. | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-mapping:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TABLE hs_office_contact_legacy_id | ||||
| ( | ||||
|     uuid        uuid NOT NULL REFERENCES hs_office_contact(uuid), | ||||
|     contact_id  integer NOT NULL | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-sequence:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE SEQUENCE IF NOT EXISTS hs_office_contact_legacy_id_seq | ||||
|     AS integer | ||||
|     START 1000000000 | ||||
|     OWNED BY hs_office_contact_legacy_id.contact_id; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-default:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| ALTER TABLE hs_office_contact_legacy_id | ||||
|     ALTER COLUMN contact_id | ||||
|         SET DEFAULT nextVal('hs_office_contact_legacy_id_seq'); | ||||
|  | ||||
| --/ | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-insert:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CALL defineContext('schema-migration'); | ||||
| INSERT INTO hs_office_contact_legacy_id(uuid, contact_id) | ||||
|     SELECT uuid, nextVal('hs_office_contact_legacy_id_seq') FROM hs_office_contact; | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function insertContactLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'INSERT' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     INSERT INTO hs_office_contact_legacy_id VALUES | ||||
|         (NEW.uuid, nextVal('hs_office_contact_legacy_id_seq')); | ||||
|  | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger createContactLegacyIdMapping | ||||
|     after insert on hs_office_contact | ||||
|         for each row | ||||
|             execute procedure insertContactLegacyIdMapping(); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-MIGRATION-delete-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function deleteContactLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'DELETE' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_contact_legacy_id | ||||
|            WHERE uuid = OLD.uuid; | ||||
|  | ||||
|     return OLD; | ||||
| end; $$; | ||||
|  | ||||
| create trigger removeContactLegacyIdMapping | ||||
|     before delete on hs_office_contact | ||||
|         for each row | ||||
|             execute procedure deleteContactLegacyIdMapping(); | ||||
| --/ | ||||
| @@ -0,0 +1,75 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single contact test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeContactTestData(contLabel varchar) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask   varchar; | ||||
|     emailAddr varchar; | ||||
| begin | ||||
|     currentTask = 'creating contact test-data ' || contLabel; | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     emailAddr = 'contact-admin@' || cleanIdentifier(contLabel) || '.example.com'; | ||||
|     call defineContext(currentTask); | ||||
|     perform createRbacUser(emailAddr); | ||||
|     call defineContext(currentTask, null, emailAddr); | ||||
|  | ||||
|     raise notice 'creating test contact: %', contLabel; | ||||
|     insert | ||||
|         into hs_office_contact (label, postaladdress, emailaddresses, phonenumbers) | ||||
|         values (contLabel, $address$ | ||||
| Vorname Nachname | ||||
| Straße Hnr | ||||
| PLZ Stadt | ||||
| $address$, emailAddr, '+49 123 1234567'); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
| /* | ||||
|     Creates a range of test contact for mass data generation. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeContactTestData( | ||||
|     startCount integer,  -- count of auto generated rows before the run | ||||
|     endCount integer     -- count of auto generated rows after the run | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
| begin | ||||
|     for t in startCount..endCount | ||||
|         loop | ||||
|             call createHsOfficeContactTestData(intToVarChar(t, 4) || '#' || t); | ||||
|             commit; | ||||
|         end loop; | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-contact-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         -- TODO: use better names | ||||
|         call createHsOfficeContactTestData('first contact'); | ||||
|         call createHsOfficeContactTestData('second contact'); | ||||
|         call createHsOfficeContactTestData('third contact'); | ||||
|         call createHsOfficeContactTestData('fourth contact'); | ||||
|         call createHsOfficeContactTestData('fifth contact'); | ||||
|         call createHsOfficeContactTestData('sixth contact'); | ||||
|         call createHsOfficeContactTestData('seventh contact'); | ||||
|         call createHsOfficeContactTestData('eighth contact'); | ||||
|         call createHsOfficeContactTestData('ninth contact'); | ||||
|         call createHsOfficeContactTestData('tenth contact'); | ||||
|         call createHsOfficeContactTestData('eleventh contact'); | ||||
|         call createHsOfficeContactTestData('twelfth contact'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,32 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-person-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TYPE HsOfficePersonType AS ENUM ( | ||||
|     '??',   -- unknown | ||||
|     'NP',   -- natural person | ||||
|     'LP',   -- legal person | ||||
|     'IF',   -- incorporated firm | ||||
|     'UF',   -- unincorporated firm | ||||
|     'PI');  -- public institution | ||||
|  | ||||
| CREATE CAST (character varying as HsOfficePersonType) WITH INOUT AS IMPLICIT; | ||||
|  | ||||
| create table if not exists hs_office_person | ||||
| ( | ||||
|     uuid           uuid unique references RbacObject (uuid) initially deferred, | ||||
|     personType     HsOfficePersonType not null, | ||||
|     tradeName      varchar(96), | ||||
|     givenName      varchar(48), | ||||
|     familyName     varchar(48) | ||||
| ); | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-person-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_person'); | ||||
| --// | ||||
| @@ -0,0 +1,45 @@ | ||||
| ### 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:INSERT{{person:INSERT}} | ||||
|         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:global:GUEST ==> perm:person:INSERT | ||||
| role:person:OWNER ==> perm:person:DELETE | ||||
| role:person:ADMIN ==> perm:person:UPDATE | ||||
| role:person:REFERRER ==> perm:person:SELECT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,146 @@ | ||||
| --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:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_person permissions for the related global rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row global; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_person permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_person'), | ||||
|                     globalGUEST()); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_person INSERT permission to specified role of new global rows. | ||||
| */ | ||||
| create or replace function hs_office_person_global_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_person'), | ||||
|             globalGUEST()); | ||||
|     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_person_global_insert_tg | ||||
|     after insert on global | ||||
|     for each row | ||||
| execute procedure hs_office_person_global_insert_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$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,77 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-person-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single person test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficePersonTestData( | ||||
|         newPersonType HsOfficePersonType, | ||||
|         newTradeName varchar, | ||||
|         newFamilyName varchar = null, | ||||
|         newGivenName varchar = null | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     fullName    varchar; | ||||
|     currentTask varchar; | ||||
|     emailAddr   varchar; | ||||
| begin | ||||
|     fullName := concat_ws(', ', newTradeName, newFamilyName, newGivenName); | ||||
|     currentTask = 'creating person test-data ' || fullName; | ||||
|     emailAddr = 'person-' || left(cleanIdentifier(fullName), 32) || '@example.com'; | ||||
|     call defineContext(currentTask); | ||||
|     perform createRbacUser(emailAddr); | ||||
|     call defineContext(currentTask, null, emailAddr); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     raise notice 'creating test person: % by %', fullName, emailAddr; | ||||
|     insert | ||||
|         into hs_office_person (persontype, tradename, givenname, familyname) | ||||
|         values (newPersonType, newTradeName, newGivenName, newFamilyName); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
| /* | ||||
|     Creates a range of test persons for mass data generation. | ||||
|  */ | ||||
| create or replace procedure createTestPersonTestData( | ||||
|     startCount integer,  -- count of auto generated rows before the run | ||||
|     endCount integer     -- count of auto generated rows after the run | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
| begin | ||||
|     for t in startCount..endCount | ||||
|         loop | ||||
|             call createHsOfficePersonTestData('LP', intToVarChar(t, 4)); | ||||
|             commit; | ||||
|         end loop; | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-person-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficePersonTestData('LP', 'Hostsharing eG'); | ||||
|         call createHsOfficePersonTestData('LP', 'First GmbH'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Firby', 'Susan'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Smith', 'Peter'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Tucker', 'Jack'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Fouler', 'Ellie'); | ||||
|         call createHsOfficePersonTestData('LP', 'Second e.K.', 'Smith', 'Peter'); | ||||
|         call createHsOfficePersonTestData('IF', 'Third OHG'); | ||||
|         call createHsOfficePersonTestData('LP', 'Fourth eG'); | ||||
|         call createHsOfficePersonTestData('UF', 'Erben Bessler', 'Mel', 'Bessler'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Bessler', 'Anita'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Bessler', 'Bert'); | ||||
|         call createHsOfficePersonTestData('NP', null, 'Winkler', 'Paul'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,36 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-relation-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TYPE HsOfficeRelationType AS ENUM ( | ||||
|     'UNKNOWN', | ||||
|     'PARTNER', | ||||
|     'EX_PARTNER', | ||||
|     'REPRESENTATIVE', | ||||
|     'DEBITOR', | ||||
|     'VIP_CONTACT', | ||||
|     'OPERATIONS', | ||||
|     'SUBSCRIBER'); | ||||
|  | ||||
| CREATE CAST (character varying as HsOfficeRelationType) WITH INOUT AS IMPLICIT; | ||||
|  | ||||
| create table if not exists hs_office_relation | ||||
| ( | ||||
|     uuid             uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade | ||||
|     anchorUuid       uuid not null references hs_office_person(uuid), | ||||
|     holderUuid       uuid not null references hs_office_person(uuid), | ||||
|     contactUuid      uuid references hs_office_contact(uuid), | ||||
|     type             HsOfficeRelationType not null, | ||||
|     mark             varchar(24) | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-relation-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_relation'); | ||||
| --// | ||||
| @@ -0,0 +1,102 @@ | ||||
| ### 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}} | ||||
|         perm:relation:INSERT{{relation:INSERT}} | ||||
|     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 | ||||
| role:anchorPerson:ADMIN ==> perm:relation:INSERT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,271 @@ | ||||
| --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; | ||||
|     assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid    INTO newAnchorPerson; | ||||
|     assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid    INTO newContact; | ||||
|     assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); | ||||
|  | ||||
|  | ||||
|     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 $$ | ||||
|  | ||||
| declare | ||||
|     oldHolderPerson hs_office_person; | ||||
|     newHolderPerson hs_office_person; | ||||
|     oldAnchorPerson hs_office_person; | ||||
|     newAnchorPerson hs_office_person; | ||||
|     oldContact hs_office_contact; | ||||
|     newContact hs_office_contact; | ||||
|  | ||||
| begin | ||||
|     call enterTriggerForObjectUuid(NEW.uuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_person WHERE uuid = OLD.holderUuid    INTO oldHolderPerson; | ||||
|     assert oldHolderPerson.uuid is not null, format('oldHolderPerson must not be null for OLD.holderUuid = %s', OLD.holderUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_person WHERE uuid = NEW.holderUuid    INTO newHolderPerson; | ||||
|     assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_person WHERE uuid = OLD.anchorUuid    INTO oldAnchorPerson; | ||||
|     assert oldAnchorPerson.uuid is not null, format('oldAnchorPerson must not be null for OLD.anchorUuid = %s', OLD.anchorUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid    INTO newAnchorPerson; | ||||
|     assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_contact WHERE uuid = OLD.contactUuid    INTO oldContact; | ||||
|     assert oldContact.uuid is not null, format('oldContact must not be null for OLD.contactUuid = %s', OLD.contactUuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid    INTO newContact; | ||||
|     assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid); | ||||
|  | ||||
|  | ||||
|     if NEW.contactUuid <> OLD.contactUuid then | ||||
|  | ||||
|         call revokeRoleFromRole(hsOfficeRelationTENANT(OLD), hsOfficeContactADMIN(oldContact)); | ||||
|         call grantRoleToRole(hsOfficeRelationTENANT(NEW), hsOfficeContactADMIN(newContact)); | ||||
|  | ||||
|         call revokeRoleFromRole(hsOfficeContactREFERRER(oldContact), hsOfficeRelationTENANT(OLD)); | ||||
|         call grantRoleToRole(hsOfficeContactREFERRER(newContact), hsOfficeRelationTENANT(NEW)); | ||||
|  | ||||
|     end if; | ||||
|  | ||||
|     call leaveTriggerForObjectUuid(NEW.uuid); | ||||
| 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:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_relation permissions for the related hs_office_person rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row hs_office_person; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_relation permissions for the related hs_office_person rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM hs_office_person | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_relation'), | ||||
|                     hsOfficePersonADMIN(row)); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_relation INSERT permission to specified role of new hs_office_person rows. | ||||
| */ | ||||
| create or replace function hs_office_relation_hs_office_person_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_relation'), | ||||
|             hsOfficePersonADMIN(NEW)); | ||||
|     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_relation_hs_office_person_insert_tg | ||||
|     after insert on hs_office_person | ||||
|     for each row | ||||
| execute procedure hs_office_relation_hs_office_person_insert_tf(); | ||||
|  | ||||
| /** | ||||
|     Checks if the user or assumed roles are allowed to insert a row to hs_office_relation, | ||||
|     where the check is performed by a direct role. | ||||
|  | ||||
|     A direct role is a role depending on a foreign key directly available in the NEW row. | ||||
| */ | ||||
| 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 hasInsertPermission(NEW.anchorUuid, 'INSERT', 'hs_office_relation') ) | ||||
|         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$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,113 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-relation-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single relation test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeRelationTestData( | ||||
|         holderPersonName varchar, | ||||
|         relationType HsOfficeRelationType, | ||||
|         anchorPersonName varchar, | ||||
|         contactLabel varchar, | ||||
|         mark varchar default null) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask     varchar; | ||||
|     idName          varchar; | ||||
|     anchorPerson    hs_office_person; | ||||
|     holderPerson    hs_office_person; | ||||
|     contact         hs_office_contact; | ||||
|  | ||||
| begin | ||||
|     idName := cleanIdentifier( anchorPersonName || '-' || holderPersonName); | ||||
|     currentTask := 'creating relation test-data ' || idName; | ||||
|     call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     select p.* | ||||
|             into anchorPerson | ||||
|             from hs_office_person p | ||||
|             where p.tradeName = anchorPersonName or p.familyName = anchorPersonName; | ||||
|     if anchorPerson is null then | ||||
|         raise exception 'anchorPerson "%" not found', anchorPersonName; | ||||
|     end if; | ||||
|  | ||||
|     select p.* | ||||
|             into holderPerson | ||||
|             from hs_office_person p | ||||
|             where p.tradeName = holderPersonName or p.familyName = holderPersonName; | ||||
|     if holderPerson is null then | ||||
|         raise exception 'holderPerson "%" not found', holderPersonName; | ||||
|     end if; | ||||
|  | ||||
|     select c.* into contact from hs_office_contact c where c.label = contactLabel; | ||||
|     if contact is null then | ||||
|         raise exception 'contact "%" not found', contactLabel; | ||||
|     end if; | ||||
|  | ||||
|     raise notice 'creating test relation: %', idName; | ||||
|     raise notice '- using anchor person (%): %', anchorPerson.uuid, anchorPerson; | ||||
|     raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson; | ||||
|     raise notice '- using contact (%): %', contact.uuid, contact; | ||||
|     insert | ||||
|         into hs_office_relation (uuid, anchoruuid, holderuuid, type, mark, contactUuid) | ||||
|         values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationType, mark, contact.uuid); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
| /* | ||||
|     Creates a range of test relation for mass data generation. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeRelationTestData( | ||||
|     startCount integer,  -- count of auto generated rows before the run | ||||
|     endCount integer     -- count of auto generated rows after the run | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     person hs_office_person; | ||||
|     contact hs_office_contact; | ||||
| begin | ||||
|     for t in startCount..endCount | ||||
|         loop | ||||
|             select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person; | ||||
|             select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact; | ||||
|  | ||||
|             call createHsOfficeRelationTestData(person.uuid, contact.uuid, 'REPRESENTATIVE'); | ||||
|             commit; | ||||
|         end loop; | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-relation-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeRelationTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact'); | ||||
|         call createHsOfficeRelationTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact'); | ||||
|         call createHsOfficeRelationTestData('First GmbH', 'DEBITOR', 'First GmbH', 'first contact'); | ||||
|  | ||||
|         call createHsOfficeRelationTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact'); | ||||
|         call createHsOfficeRelationTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact'); | ||||
|         call createHsOfficeRelationTestData('Second e.K.', 'DEBITOR', 'Second e.K.', 'second contact'); | ||||
|  | ||||
|         call createHsOfficeRelationTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact'); | ||||
|         call createHsOfficeRelationTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact'); | ||||
|         call createHsOfficeRelationTestData('Third OHG', 'DEBITOR', 'Third OHG', 'third contact'); | ||||
|  | ||||
|         call createHsOfficeRelationTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact'); | ||||
|         call createHsOfficeRelationTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact'); | ||||
|         call createHsOfficeRelationTestData('Third OHG', 'DEBITOR', 'Third OHG', 'third contact'); | ||||
|  | ||||
|         call createHsOfficeRelationTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact'); | ||||
|         call createHsOfficeRelationTestData('Smith', 'DEBITOR', 'Smith', 'third contact'); | ||||
|         call createHsOfficeRelationTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,85 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-DETAILS-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table hs_office_partner_details | ||||
| ( | ||||
|     uuid                uuid unique references RbacObject (uuid) initially deferred, | ||||
|     registrationOffice  varchar(96), | ||||
|     registrationNumber  varchar(96), | ||||
|     birthPlace          varchar(96), | ||||
|     birthName           varchar(96), | ||||
|     birthday            date, | ||||
|     dateOfDeath         date | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-DETAILS-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_partner_details'); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table hs_office_partner | ||||
| ( | ||||
|     uuid                uuid unique references RbacObject (uuid) initially deferred, | ||||
|     partnerNumber       numeric(5) unique not null, | ||||
|     partnerRelUuid     uuid not null references hs_office_relation(uuid), -- deleted in after delete trigger | ||||
|     detailsUuid         uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-DELETE-DEPENDENTS-TRIGGER:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /** | ||||
|     Trigger function to delete related details of a partner to delete. | ||||
|  */ | ||||
| create or replace function deleteHsOfficeDependentsOnPartnerDelete() | ||||
|     returns trigger | ||||
|     language PLPGSQL | ||||
| as $$ | ||||
| declare | ||||
|     counter integer; | ||||
| begin | ||||
|     DELETE FROM hs_office_partner_details d WHERE d.uuid = OLD.detailsUuid; | ||||
|     GET DIAGNOSTICS counter = ROW_COUNT; | ||||
|     if counter = 0 then | ||||
|         raise exception 'partner details % could not be deleted', OLD.detailsUuid; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_relation r WHERE r.uuid = OLD.partnerRelUuid; | ||||
|     GET DIAGNOSTICS counter = ROW_COUNT; | ||||
|     if counter = 0 then | ||||
|         raise exception 'partner relation % could not be deleted', OLD.partnerRelUuid; | ||||
|     end if; | ||||
|  | ||||
|     RETURN OLD; | ||||
| end; $$; | ||||
|  | ||||
| /** | ||||
|     Triggers deletion of related rows of a partner to delete. | ||||
|  */ | ||||
| create trigger hs_office_partner_delete_dependents_trigger | ||||
|     after delete | ||||
|     on hs_office_partner | ||||
|     for each row | ||||
|         execute procedure deleteHsOfficeDependentsOnPartnerDelete(); | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_partner'); | ||||
| --// | ||||
| @@ -0,0 +1,120 @@ | ||||
| ### 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: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 | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,238 @@ | ||||
| --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; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_partner'), | ||||
|                     globalADMIN()); | ||||
|             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 generateRbacIdentityViewFromProjection('hs_office_partner', | ||||
|     $idName$ | ||||
|         'P-' || partnerNumber | ||||
|     $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_partner', | ||||
|     $orderBy$ | ||||
|         'P-' || partnerNumber | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         partnerRelUuid = new.partnerRelUuid | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,23 @@ | ||||
| ### rbac partnerDetails | ||||
|  | ||||
| This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. | ||||
|  | ||||
| ```mermaid | ||||
| %%{init:{'flowchart':{'htmlLabels':false}}}%% | ||||
| flowchart TB | ||||
|  | ||||
| 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 | ||||
| end | ||||
|  | ||||
| %% granting permissions to roles | ||||
| role:global:ADMIN ==> perm:partnerDetails:INSERT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,150 @@ | ||||
| --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 | ||||
|  | ||||
| begin | ||||
|     call enterTriggerForObjectUuid(NEW.uuid); | ||||
|  | ||||
|     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; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_partner_details'), | ||||
|                     globalADMIN()); | ||||
|             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 partnerDetails.uuid as uuid, partner_iv.idName as idName | ||||
|             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$ | ||||
|         uuid | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         registrationOffice = new.registrationOffice, | ||||
|         registrationNumber = new.registrationNumber, | ||||
|         birthPlace = new.birthPlace, | ||||
|         birthName = new.birthName, | ||||
|         birthday = new.birthday, | ||||
|         dateOfDeath = new.dateOfDeath | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,95 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- TODO: These changesets are just for the external remote views to simulate the legacy tables. | ||||
| --  Once we don't need the external remote views anymore, create revert changesets. | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-mapping:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TABLE hs_office_partner_legacy_id | ||||
| ( | ||||
|     uuid        uuid NOT NULL REFERENCES hs_office_partner(uuid), | ||||
|     bp_id       integer NOT NULL | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-sequence:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE SEQUENCE IF NOT EXISTS hs_office_partner_legacy_id_seq | ||||
|     AS integer | ||||
|     START 1000000000 | ||||
|     OWNED BY hs_office_partner_legacy_id.bp_id; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-default:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| ALTER TABLE hs_office_partner_legacy_id | ||||
|     ALTER COLUMN bp_id | ||||
|         SET DEFAULT nextVal('hs_office_partner_legacy_id_seq'); | ||||
| --/ | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-insert:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CALL defineContext('schema-migration'); | ||||
| INSERT INTO hs_office_partner_legacy_id(uuid, bp_id) | ||||
|     SELECT uuid, nextVal('hs_office_partner_legacy_id_seq') FROM hs_office_partner; | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function insertPartnerLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'INSERT' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     INSERT INTO hs_office_partner_legacy_id VALUES | ||||
|         (NEW.uuid, nextVal('hs_office_partner_legacy_id_seq')); | ||||
|  | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger createPartnerLegacyIdMapping | ||||
|     after insert on hs_office_partner | ||||
|         for each row | ||||
|             execute procedure insertPartnerLegacyIdMapping(); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-MIGRATION-delete-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function deletePartnerLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'DELETE' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_partner_legacy_id | ||||
|         WHERE uuid = OLD.uuid; | ||||
|  | ||||
|     return OLD; | ||||
| end; $$; | ||||
|  | ||||
| create trigger removePartnerLegacyIdMapping | ||||
|     before delete on hs_office_partner | ||||
|     for each row | ||||
|         execute procedure deletePartnerLegacyIdMapping(); | ||||
| --/ | ||||
| @@ -0,0 +1,86 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single partner test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficePartnerTestData( | ||||
|         mandantTradeName  varchar, | ||||
|         newPartnerNumber  numeric(5), | ||||
|         partnerPersonName varchar, | ||||
|         contactLabel      varchar ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask         varchar; | ||||
|     idName              varchar; | ||||
|     mandantPerson       hs_office_person; | ||||
|     partnerRel         hs_office_relation; | ||||
|     relatedPerson       hs_office_person; | ||||
|     relatedDetailsUuid  uuid; | ||||
| begin | ||||
|     idName := cleanIdentifier( partnerPersonName|| '-' || contactLabel); | ||||
|     currentTask := 'creating partner test-data ' || idName; | ||||
|     call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     select p.* from hs_office_person p | ||||
|                where p.tradeName = mandantTradeName | ||||
|                into mandantPerson; | ||||
|     if mandantPerson is null then | ||||
|         raise exception 'mandant "%" not found', mandantTradeName; | ||||
|     end if; | ||||
|  | ||||
|     select p.* from hs_office_person p | ||||
|                where p.tradeName = partnerPersonName or p.familyName = partnerPersonName | ||||
|                into relatedPerson; | ||||
|  | ||||
|     select r.* from hs_office_relation r | ||||
|             where r.type = 'PARTNER' | ||||
|                 and r.anchoruuid = mandantPerson.uuid and r.holderuuid = relatedPerson.uuid | ||||
|             into partnerRel; | ||||
|     if partnerRel is null then | ||||
|         raise exception 'partnerRel "%"-"%" not found', mandantPerson.tradename, partnerPersonName; | ||||
|     end if; | ||||
|  | ||||
|     raise notice 'creating test partner: %', idName; | ||||
|     raise notice '- using partnerRel (%): %', partnerRel.uuid, partnerRel; | ||||
|     raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson; | ||||
|  | ||||
|     if relatedPerson.persontype = 'NP' then | ||||
|         insert | ||||
|             into hs_office_partner_details (uuid, birthName, birthday, birthPlace) | ||||
|             values (uuid_generate_v4(), 'Meyer', '1987-10-31', 'Hamburg') | ||||
|             returning uuid into relatedDetailsUuid; | ||||
|     else | ||||
|         insert | ||||
|             into hs_office_partner_details (uuid, registrationOffice, registrationNumber) | ||||
|             values (uuid_generate_v4(), 'Hamburg', 'RegNo123456789') | ||||
|             returning uuid into relatedDetailsUuid; | ||||
|     end if; | ||||
|  | ||||
|     insert | ||||
|         into hs_office_partner (uuid, partnerNumber, partnerRelUuid, detailsUuid) | ||||
|         values (uuid_generate_v4(), newPartnerNumber, partnerRel.uuid, relatedDetailsUuid); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-partner-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficePartnerTestData('Hostsharing eG', 10001, 'First GmbH', 'first contact'); | ||||
|         call createHsOfficePartnerTestData('Hostsharing eG', 10002, 'Second e.K.', 'second contact'); | ||||
|         call createHsOfficePartnerTestData('Hostsharing eG', 10003, 'Third OHG', 'third contact'); | ||||
|         call createHsOfficePartnerTestData('Hostsharing eG', 10004, 'Fourth eG', 'fourth contact'); | ||||
|         call createHsOfficePartnerTestData('Hostsharing eG', 10010, 'Smith', 'fifth contact'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,21 @@ | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table hs_office_bankaccount | ||||
| ( | ||||
|     uuid                uuid unique references RbacObject (uuid) initially deferred, | ||||
|     holder              varchar(64) not null, | ||||
|     iban                varchar(34) not null, | ||||
|     bic                 varchar(11) not null | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_bankaccount'); | ||||
| --// | ||||
| @@ -0,0 +1,45 @@ | ||||
| ### 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:INSERT{{bankAccount:INSERT}} | ||||
|         perm:bankAccount:DELETE{{bankAccount:DELETE}} | ||||
|         perm:bankAccount:UPDATE{{bankAccount:UPDATE}} | ||||
|         perm:bankAccount:SELECT{{bankAccount:SELECT}} | ||||
|     end | ||||
| end | ||||
|  | ||||
| %% granting roles to users | ||||
| user:creator ==> role:bankAccount:OWNER | ||||
|  | ||||
| %% granting roles to roles | ||||
| role:global:ADMIN ==> role:bankAccount:OWNER | ||||
| role:bankAccount:OWNER ==> role:bankAccount:ADMIN | ||||
| role:bankAccount:ADMIN ==> role:bankAccount:REFERRER | ||||
|  | ||||
| %% granting permissions to roles | ||||
| role:global:GUEST ==> perm:bankAccount:INSERT | ||||
| role:bankAccount:OWNER ==> perm:bankAccount:DELETE | ||||
| role:bankAccount:ADMIN ==> perm:bankAccount:UPDATE | ||||
| role:bankAccount:REFERRER ==> perm:bankAccount:SELECT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,145 @@ | ||||
| --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:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_bankaccount permissions for the related global rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row global; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_bankaccount permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'), | ||||
|                     globalGUEST()); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_bankaccount INSERT permission to specified role of new global rows. | ||||
| */ | ||||
| create or replace function hs_office_bankaccount_global_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'), | ||||
|             globalGUEST()); | ||||
|     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_bankaccount_global_insert_tg | ||||
|     after insert on global | ||||
|     for each row | ||||
| execute procedure hs_office_bankaccount_global_insert_tf(); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call generateRbacIdentityViewFromProjection('hs_office_bankaccount', | ||||
|     $idName$ | ||||
|         iban | ||||
|     $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_bankaccount', | ||||
|     $orderBy$ | ||||
|         iban | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         holder = new.holder, | ||||
|         iban = new.iban, | ||||
|         bic = new.bic | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,49 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single bankaccount test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeBankAccountTestData(givenHolder varchar, givenIBAN varchar, givenBIC varchar) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask   varchar; | ||||
|     emailAddr varchar; | ||||
| begin | ||||
|     currentTask = 'creating bankaccount test-data ' || givenHolder; | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     emailAddr = 'bankaccount-admin@' || cleanIdentifier(givenHolder) || '.example.com'; | ||||
|     call defineContext(currentTask); | ||||
|     perform createRbacUser(emailAddr); | ||||
|     call defineContext(currentTask, null, emailAddr); | ||||
|  | ||||
|     raise notice 'creating test bankaccount: %', givenHolder; | ||||
|     insert | ||||
|         into hs_office_bankaccount(uuid, holder, iban, bic) | ||||
|         values (uuid_generate_v4(), givenHolder, givenIBAN, givenBIC); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-bankaccount-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         -- IBANs+BICs taken from https://ibanvalidieren.de/beispiele.html | ||||
|         call createHsOfficeBankAccountTestData('First GmbH', 'DE02120300000000202051', 'BYLADEM1001'); | ||||
|         call createHsOfficeBankAccountTestData('Peter Smith', 'DE02500105170137075030', 'INGDDEFF'); | ||||
|         call createHsOfficeBankAccountTestData('Second e.K.', 'DE02100500000054540402', 'BELADEBE'); | ||||
|         call createHsOfficeBankAccountTestData('Third OHG', 'DE02300209000106531065', 'CMCIDEDD'); | ||||
|         call createHsOfficeBankAccountTestData('Fourth eG', 'DE02200505501015871393', 'HASPDEHH'); | ||||
|         call createHsOfficeBankAccountTestData('Mel Bessler', 'DE02100100100006820101', 'PBNKDEFF'); | ||||
|         call createHsOfficeBankAccountTestData('Anita Bessler', 'DE02300606010002474689', 'DAAEDEDD'); | ||||
|         call createHsOfficeBankAccountTestData('Paul Winkler', 'DE02600501010002034304', 'SOLADEST600'); | ||||
|     end; | ||||
| $$; | ||||
| @@ -0,0 +1,64 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table hs_office_debitor | ||||
| ( | ||||
|     uuid                    uuid unique references RbacObject (uuid) initially deferred, | ||||
|     debitorNumberSuffix     numeric(2) not null, | ||||
|     debitorRelUuid          uuid not null references hs_office_relation(uuid), | ||||
|     billable                boolean not null default true, | ||||
|     vatId                   varchar(24), -- TODO.spec: here or in person? | ||||
|     vatCountryCode          varchar(2), | ||||
|     vatBusiness             boolean not null, | ||||
|     vatReverseCharge        boolean not null, | ||||
|     refundBankAccountUuid   uuid references hs_office_bankaccount(uuid), | ||||
|     defaultPrefix           char(3) not null unique | ||||
|             constraint check_default_prefix check ( | ||||
|                 defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$' | ||||
|                 ) | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-DELETE-DEPENDENTS-TRIGGER:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /** | ||||
|     Trigger function to delete related rows of a debitor to delete. | ||||
|  */ | ||||
| create or replace function deleteHsOfficeDependentsOnDebitorDelete() | ||||
|     returns trigger | ||||
|     language PLPGSQL | ||||
| as $$ | ||||
| declare | ||||
|     counter integer; | ||||
| begin | ||||
|     DELETE FROM hs_office_relation r WHERE r.uuid = OLD.debitorRelUuid; | ||||
|     GET DIAGNOSTICS counter = ROW_COUNT; | ||||
|     if counter = 0 then | ||||
|         raise exception 'debitor relation % could not be deleted', OLD.debitorRelUuid; | ||||
|     end if; | ||||
|  | ||||
|     RETURN OLD; | ||||
| end; $$; | ||||
|  | ||||
| /** | ||||
|     Triggers deletion of related details of a debitor to delete. | ||||
|  */ | ||||
| create trigger hs_office_debitor_delete_dependents_trigger | ||||
|     after delete | ||||
|     on hs_office_debitor | ||||
|     for each row | ||||
| execute procedure deleteHsOfficeDependentsOnDebitorDelete(); | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_debitor'); | ||||
| --// | ||||
| @@ -0,0 +1,198 @@ | ||||
| ### 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: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: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 | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,227 @@ | ||||
| --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 partnerRel.* | ||||
|         FROM hs_office_relation AS partnerRel | ||||
|         JOIN hs_office_relation AS debitorRel | ||||
|             ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid | ||||
|         WHERE partnerRel.type = 'PARTNER' | ||||
|             AND NEW.debitorRelUuid = debitorRel.uuid | ||||
|         INTO newPartnerRel; | ||||
|     assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid); | ||||
|  | ||||
|     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.debitorRelUuid is distinct from OLD.debitorRelUuid | ||||
|     or 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; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_debitor'), | ||||
|                     globalADMIN()); | ||||
|             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 AS 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 debitorRel.type = 'DEBITOR' | ||||
|                             WHERE debitorRel.uuid = debitor.debitorRelUuid) | ||||
|                          || to_char(debitorNumberSuffix, 'fm00') as idName | ||||
|         FROM hs_office_debitor AS debitor | ||||
|         $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_debitor', | ||||
|     $orderBy$ | ||||
|         defaultPrefix | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         debitorRelUuid = new.debitorRelUuid, | ||||
|         billable = new.billable, | ||||
|         refundBankAccountUuid = new.refundBankAccountUuid, | ||||
|         vatId = new.vatId, | ||||
|         vatCountryCode = new.vatCountryCode, | ||||
|         vatBusiness = new.vatBusiness, | ||||
|         vatReverseCharge = new.vatReverseCharge, | ||||
|         defaultPrefix = new.defaultPrefix | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,62 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single debitor test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeDebitorTestData( | ||||
|         withDebitorNumberSuffix numeric(5), | ||||
|         forPartnerPersonName varchar, | ||||
|         forBillingContactLabel varchar, | ||||
|         withDefaultPrefix varchar | ||||
|     ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask             varchar; | ||||
|     idName                  varchar; | ||||
|     relatedDebitorRelUuid   uuid; | ||||
|     relatedBankAccountUuid  uuid; | ||||
| begin | ||||
|     idName := cleanIdentifier( forPartnerPersonName|| '-' || forBillingContactLabel); | ||||
|     currentTask := 'creating debitor test-data ' || idName; | ||||
|     call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     select debitorRel.uuid | ||||
|             into relatedDebitorRelUuid | ||||
|             from hs_office_relation debitorRel | ||||
|             join hs_office_person person on person.uuid = debitorRel.holderUuid | ||||
|                 and (person.tradeName = forPartnerPersonName or person.familyName = forPartnerPersonName) | ||||
|             where debitorRel.type = 'DEBITOR'; | ||||
|  | ||||
|     select b.uuid | ||||
|             into relatedBankAccountUuid | ||||
|             from hs_office_bankaccount b | ||||
|             where b.holder = forPartnerPersonName; | ||||
|  | ||||
|     raise notice 'creating test debitor: % (#%)', idName, withDebitorNumberSuffix; | ||||
|     -- raise exception 'creating test debitor: (uuid=%, debitorRelUuid=%, debitornumbersuffix=%, billable=%, vatbusiness=%, vatreversecharge=%, refundbankaccountuuid=%, defaultprefix=%)', | ||||
|     --    uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true,     true,        false,            relatedBankAccountUuid, withDefaultPrefix; | ||||
|     insert | ||||
|         into hs_office_debitor (uuid,   debitorRelUuid,        debitornumbersuffix,     billable, vatbusiness, vatreversecharge, refundbankaccountuuid,  defaultprefix) | ||||
|             values (uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true,     true,        false,            relatedBankAccountUuid, withDefaultPrefix); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-debitor-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeDebitorTestData(11, 'First GmbH', 'first contact', 'fir'); | ||||
|         call createHsOfficeDebitorTestData(12, 'Second e.K.', 'second contact', 'sec'); | ||||
|         call createHsOfficeDebitorTestData(13, 'Third OHG', 'third contact', 'thi'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,24 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create table if not exists hs_office_sepamandate | ||||
| ( | ||||
|     uuid                uuid unique references RbacObject (uuid) initially deferred, | ||||
|     debitorUuid         uuid not null references hs_office_debitor(uuid), | ||||
|     bankAccountUuid     uuid not null references hs_office_bankaccount(uuid), | ||||
|     reference           varchar(96) not null, | ||||
|     agreement           date not null, | ||||
|     validity            daterange not null | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_sepamandate'); | ||||
| --// | ||||
| @@ -0,0 +1,141 @@ | ||||
| ### 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}} | ||||
|         perm:sepaMandate:INSERT{{sepaMandate:INSERT}} | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph debitorRel["`**debitorRel**`"] | ||||
|     direction TB | ||||
|     style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     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 | ||||
| role:debitorRel:ADMIN ==> perm:sepaMandate:INSERT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,207 @@ | ||||
| --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; | ||||
|     assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s', NEW.bankAccountUuid); | ||||
|  | ||||
|     SELECT debitorRel.* | ||||
|         FROM hs_office_relation debitorRel | ||||
|         JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid | ||||
|         WHERE debitor.uuid = NEW.debitorUuid | ||||
|         INTO newDebitorRel; | ||||
|     assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid); | ||||
|  | ||||
|  | ||||
|     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:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_sepamandate permissions for the related hs_office_relation rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row hs_office_relation; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_sepamandate permissions for the related hs_office_relation rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM hs_office_relation | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_sepamandate'), | ||||
|                     hsOfficeRelationADMIN(row)); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_sepamandate INSERT permission to specified role of new hs_office_relation rows. | ||||
| */ | ||||
| create or replace function hs_office_sepamandate_hs_office_relation_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_sepamandate'), | ||||
|             hsOfficeRelationADMIN(NEW)); | ||||
|     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_sepamandate_hs_office_relation_insert_tg | ||||
|     after insert on hs_office_relation | ||||
|     for each row | ||||
| execute procedure hs_office_sepamandate_hs_office_relation_insert_tf(); | ||||
|  | ||||
| /** | ||||
|     Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate, | ||||
|     where the check is performed by an indirect role. | ||||
|  | ||||
|     An indirect role is a role which depends on an object uuid which is not a direct foreign key | ||||
|     of the source entity, but needs to be fetched via joined tables. | ||||
| */ | ||||
| create or replace function hs_office_sepamandate_insert_permission_check_tf() | ||||
|     returns trigger | ||||
|     language plpgsql as $$ | ||||
|  | ||||
| declare | ||||
|     superRoleObjectUuid uuid; | ||||
|  | ||||
| begin | ||||
|         superRoleObjectUuid := (SELECT debitorRel.uuid | ||||
|             FROM hs_office_relation debitorRel | ||||
|             JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid | ||||
|             WHERE debitor.uuid = NEW.debitorUuid | ||||
|         ); | ||||
|         assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null'; | ||||
|  | ||||
|         if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', 'hs_office_sepamandate') ) then | ||||
|             raise exception | ||||
|                 '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)', | ||||
|                 currentSubjects(), currentSubjectsUuids(); | ||||
|     end if; | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger hs_office_sepamandate_insert_permission_check_tg | ||||
|     before insert on hs_office_sepamandate | ||||
|     for each row | ||||
|         execute procedure hs_office_sepamandate_insert_permission_check_tf(); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
|     call generateRbacIdentityViewFromQuery('hs_office_sepamandate', | ||||
|         $idName$ | ||||
|             select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName | ||||
|             from hs_office_sepamandate sm | ||||
|             join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid | ||||
|         $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_sepamandate', | ||||
|     $orderBy$ | ||||
|         validity | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         reference = new.reference, | ||||
|         agreement = new.agreement, | ||||
|         validity = new.validity | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,97 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- TODO: These changesets are just for the external remote views to simulate the legacy tables. | ||||
| --  Once we don't need the external remote views anymore, create revert changesets. | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-mapping:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TABLE hs_office_sepamandate_legacy_id | ||||
| ( | ||||
|     uuid            uuid NOT NULL REFERENCES hs_office_sepamandate(uuid), | ||||
|     sepa_mandate_id  integer NOT NULL | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-sequence:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE SEQUENCE IF NOT EXISTS hs_office_sepamandate_legacy_id_seq | ||||
|     AS integer | ||||
|     START 1000000000 | ||||
|     OWNED BY hs_office_sepamandate_legacy_id.sepa_mandate_id; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-default:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| ALTER TABLE hs_office_sepamandate_legacy_id | ||||
|     ALTER COLUMN sepa_mandate_id | ||||
|         SET DEFAULT nextVal('hs_office_sepamandate_legacy_id_seq'); | ||||
|  | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-insert:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CALL defineContext('schema-migration'); | ||||
| INSERT INTO hs_office_sepamandate_legacy_id(uuid, sepa_mandate_id) | ||||
|     SELECT uuid, nextVal('hs_office_sepamandate_legacy_id_seq') FROM hs_office_sepamandate; | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function insertSepaMandateLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'INSERT' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     INSERT INTO hs_office_sepamandate_legacy_id VALUES | ||||
|         (NEW.uuid, nextVal('hs_office_sepamandate_legacy_id_seq')); | ||||
|  | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger createSepaMandateLegacyIdMapping | ||||
|     after insert on hs_office_sepamandate | ||||
|         for each row | ||||
|             execute procedure insertSepaMandateLegacyIdMapping(); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepamandate-MIGRATION-delete-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function deleteSepaMandateLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'DELETE' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_sepamandate_legacy_id | ||||
|            WHERE uuid = OLD.uuid; | ||||
|  | ||||
|     return OLD; | ||||
| end; $$; | ||||
|  | ||||
| create trigger removeSepaMandateLegacyIdMapping | ||||
|     before delete on hs_office_sepamandate | ||||
|         for each row | ||||
|             execute procedure deleteSepaMandateLegacyIdMapping(); | ||||
| --/ | ||||
| @@ -0,0 +1,56 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepaMandate-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single sepaMandate test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeSepaMandateTestData( | ||||
|         forPartnerNumber numeric(5), | ||||
|         forDebitorSuffix numeric(2), | ||||
|         forIban varchar, | ||||
|         withReference varchar) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask         varchar; | ||||
|     relatedDebitor      hs_office_debitor; | ||||
|     relatedBankAccount  hs_office_bankAccount; | ||||
| begin | ||||
|     currentTask := 'creating SEPA-mandate test-data ' || forPartnerNumber::text || forDebitorSuffix::text; | ||||
|     call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     select debitor.* into relatedDebitor | ||||
|         from hs_office_debitor debitor | ||||
|         join hs_office_relation debitorRel on debitorRel.uuid = debitor.debitorRelUuid | ||||
|         join hs_office_relation partnerRel on partnerRel.holderUuid = debitorRel.anchorUuid | ||||
|         join hs_office_partner partner on partner.partnerRelUuid = partnerRel.uuid | ||||
|         where partner.partnerNumber = forPartnerNumber and debitor.debitorNumberSuffix = forDebitorSuffix; | ||||
|     select b.* into relatedBankAccount | ||||
|         from hs_office_bankAccount b where b.iban = forIban; | ||||
|  | ||||
|     raise notice 'creating test SEPA-mandate: %', forPartnerNumber::text || forDebitorSuffix::text; | ||||
|     raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; | ||||
|     raise notice '- using bankAccount (%): %', relatedBankAccount.uuid, relatedBankAccount; | ||||
|     insert | ||||
|         into hs_office_sepamandate (uuid, debitoruuid, bankAccountuuid, reference, agreement, validity) | ||||
|         values (uuid_generate_v4(), relatedDebitor.uuid, relatedBankAccount.uuid, withReference, '20220930', daterange('20221001' , '20261231', '[]')); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-sepaMandate-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeSepaMandateTestData(10001, 11, 'DE02120300000000202051', 'ref-10001-11'); | ||||
|         call createHsOfficeSepaMandateTestData(10002, 12, 'DE02100500000054540402', 'ref-10002-12'); | ||||
|         call createHsOfficeSepaMandateTestData(10003, 13, 'DE02300209000106531065', 'ref-10003-13'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,31 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TYPE HsOfficeReasonForTermination AS ENUM ('NONE', 'CANCELLATION', 'TRANSFER', 'DEATH', 'LIQUIDATION', 'EXPULSION', 'UNKNOWN'); | ||||
|  | ||||
| CREATE CAST (character varying as HsOfficeReasonForTermination) WITH INOUT AS IMPLICIT; | ||||
|  | ||||
| create table if not exists hs_office_membership | ||||
| ( | ||||
|     uuid                    uuid unique references RbacObject (uuid) initially deferred, | ||||
|     partnerUuid             uuid not null references hs_office_partner(uuid), | ||||
|     memberNumberSuffix      char(2) not null check ( | ||||
|         memberNumberSuffix::text ~ '^[0-9][0-9]$'), | ||||
|     validity                daterange not null, | ||||
|     reasonForTermination    HsOfficeReasonForTermination not null default 'NONE', | ||||
|     membershipFeeBillable   boolean not null default true, | ||||
|  | ||||
|     UNIQUE(partnerUuid, memberNumberSuffix) | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_membership'); | ||||
| --// | ||||
| @@ -0,0 +1,120 @@ | ||||
| ### rbac membership | ||||
|  | ||||
| This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. | ||||
|  | ||||
| ```mermaid | ||||
| %%{init:{'flowchart':{'htmlLabels':false}}}%% | ||||
| flowchart TB | ||||
|  | ||||
| subgraph partnerRel["`**partnerRel**`"] | ||||
|     direction TB | ||||
|     style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     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 membership["`**membership**`"] | ||||
|     direction TB | ||||
|     style membership fill:#dd4901,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership:roles[ ] | ||||
|         style membership:roles fill:#dd4901,stroke:white | ||||
|  | ||||
|         role:membership:OWNER[[membership:OWNER]] | ||||
|         role:membership:ADMIN[[membership:ADMIN]] | ||||
|         role:membership:AGENT[[membership:AGENT]] | ||||
|     end | ||||
|  | ||||
|     subgraph membership:permissions[ ] | ||||
|         style membership:permissions fill:#dd4901,stroke:white | ||||
|  | ||||
|         perm:membership:INSERT{{membership:INSERT}} | ||||
|         perm:membership:DELETE{{membership:DELETE}} | ||||
|         perm:membership:UPDATE{{membership:UPDATE}} | ||||
|         perm:membership:SELECT{{membership: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 users | ||||
| user:creator ==> role:membership:OWNER | ||||
|  | ||||
| %% 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 | ||||
| role:membership:OWNER ==> role:membership:ADMIN | ||||
| role:partnerRel:ADMIN ==> role:membership:ADMIN | ||||
| role:membership:ADMIN ==> role:membership:AGENT | ||||
| role:partnerRel:AGENT ==> role:membership:AGENT | ||||
| role:membership:AGENT ==> role:partnerRel:TENANT | ||||
|  | ||||
| %% granting permissions to roles | ||||
| role:global:ADMIN ==> perm:membership:INSERT | ||||
| role:membership:ADMIN ==> perm:membership:DELETE | ||||
| role:membership:ADMIN ==> perm:membership:UPDATE | ||||
| role:membership:AGENT ==> perm:membership:SELECT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,178 @@ | ||||
| --liquibase formatted sql | ||||
| -- This code generated was by RbacViewPostgresGenerator, do not amend manually. | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-OBJECT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRelatedRbacObject('hs_office_membership'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRoleDescriptors('hsOfficeMembership', 'hs_office_membership'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates the roles, grants and permission for the AFTER INSERT TRIGGER. | ||||
|  */ | ||||
|  | ||||
| create or replace procedure buildRbacSystemForHsOfficeMembership( | ||||
|     NEW hs_office_membership | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
|  | ||||
| declare | ||||
|     newPartnerRel hs_office_relation; | ||||
|  | ||||
| begin | ||||
|     call enterTriggerForObjectUuid(NEW.uuid); | ||||
|  | ||||
|     SELECT partnerRel.* | ||||
|         FROM hs_office_partner AS partner | ||||
|         JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid | ||||
|         WHERE partner.uuid = NEW.partnerUuid | ||||
|         INTO newPartnerRel; | ||||
|     assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid); | ||||
|  | ||||
|  | ||||
|     perform createRoleWithGrants( | ||||
|         hsOfficeMembershipOWNER(NEW), | ||||
|             userUuids => array[currentUserUuid()] | ||||
|     ); | ||||
|  | ||||
|     perform createRoleWithGrants( | ||||
|         hsOfficeMembershipADMIN(NEW), | ||||
|             permissions => array['DELETE', 'UPDATE'], | ||||
|             incomingSuperRoles => array[ | ||||
|             	hsOfficeMembershipOWNER(NEW), | ||||
|             	hsOfficeRelationADMIN(newPartnerRel)] | ||||
|     ); | ||||
|  | ||||
|     perform createRoleWithGrants( | ||||
|         hsOfficeMembershipAGENT(NEW), | ||||
|             permissions => array['SELECT'], | ||||
|             incomingSuperRoles => array[ | ||||
|             	hsOfficeMembershipADMIN(NEW), | ||||
|             	hsOfficeRelationAGENT(newPartnerRel)], | ||||
|             outgoingSubRoles => array[hsOfficeRelationTENANT(newPartnerRel)] | ||||
|     ); | ||||
|  | ||||
|     call leaveTriggerForObjectUuid(NEW.uuid); | ||||
| end; $$; | ||||
|  | ||||
| /* | ||||
|     AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_membership row. | ||||
|  */ | ||||
|  | ||||
| create or replace function insertTriggerForHsOfficeMembership_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call buildRbacSystemForHsOfficeMembership(NEW); | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger insertTriggerForHsOfficeMembership_tg | ||||
|     after insert on hs_office_membership | ||||
|     for each row | ||||
| execute procedure insertTriggerForHsOfficeMembership_tf(); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-INSERT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_membership permissions for the related global rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row global; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_membership permissions for the related global rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM global | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_membership'), | ||||
|                     globalADMIN()); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_membership INSERT permission to specified role of new global rows. | ||||
| */ | ||||
| create or replace function hs_office_membership_global_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_membership'), | ||||
|             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_membership_global_insert_tg | ||||
|     after insert on global | ||||
|     for each row | ||||
| execute procedure hs_office_membership_global_insert_tf(); | ||||
|  | ||||
| /** | ||||
|     Checks if the user or assumed roles are allowed to insert a row to hs_office_membership, | ||||
|     where only global-admin has that permission. | ||||
| */ | ||||
| create or replace function hs_office_membership_insert_permission_missing_tf() | ||||
|     returns trigger | ||||
|     language plpgsql as $$ | ||||
| begin | ||||
|     raise exception '[403] insert into hs_office_membership not allowed for current subjects % (%)', | ||||
|         currentSubjects(), currentSubjectsUuids(); | ||||
| end; $$; | ||||
|  | ||||
| create trigger hs_office_membership_insert_permission_check_tg | ||||
|     before insert on hs_office_membership | ||||
|     for each row | ||||
|     when ( not isGlobalAdmin() ) | ||||
|         execute procedure hs_office_membership_insert_permission_missing_tf(); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
|     call generateRbacIdentityViewFromQuery('hs_office_membership', | ||||
|         $idName$ | ||||
|             SELECT m.uuid AS uuid, | ||||
|                 'M-' || p.partnerNumber || m.memberNumberSuffix as idName | ||||
|         FROM hs_office_membership AS m | ||||
|         JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid | ||||
|         $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_membership', | ||||
|     $orderBy$ | ||||
|         validity | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         validity = new.validity, | ||||
|         membershipFeeBillable = new.membershipFeeBillable, | ||||
|         reasonForTermination = new.reasonForTermination | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,48 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single membership test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeMembershipTestData( | ||||
|         forPartnerNumber numeric(5), | ||||
|         newMemberNumberSuffix char(2) ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask             varchar; | ||||
|     relatedPartner          hs_office_partner; | ||||
| begin | ||||
|     currentTask := 'creating Membership test-data ' || | ||||
|                     'P-' || forPartnerNumber::text || | ||||
|                     'M-...' || newMemberNumberSuffix; | ||||
|     call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN'); | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     select partner.* from hs_office_partner partner | ||||
|                      where partner.partnerNumber = forPartnerNumber into relatedPartner; | ||||
|  | ||||
|     raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix; | ||||
|     raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; | ||||
|     insert | ||||
|         into hs_office_membership (uuid, partneruuid, memberNumberSuffix, validity, reasonfortermination) | ||||
|         values (uuid_generate_v4(), relatedPartner.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE'); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-membership-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeMembershipTestData(10001, '01'); | ||||
|         call createHsOfficeMembershipTestData(10002, '02'); | ||||
|         call createHsOfficeMembershipTestData(10003, '03'); | ||||
|     end; | ||||
| $$; | ||||
| --// | ||||
| @@ -0,0 +1,56 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TYPE HsOfficeCoopSharesTransactionType AS ENUM ('ADJUSTMENT', 'SUBSCRIPTION', 'CANCELLATION'); | ||||
|  | ||||
| CREATE CAST (character varying as HsOfficeCoopSharesTransactionType) WITH INOUT AS IMPLICIT; | ||||
|  | ||||
| create table if not exists hs_office_coopsharestransaction | ||||
| ( | ||||
|     uuid            uuid unique references RbacObject (uuid) initially deferred, | ||||
|     membershipUuid  uuid not null references hs_office_membership(uuid), | ||||
|     transactionType HsOfficeCoopSharesTransactionType not null, | ||||
|     valueDate       date not null, | ||||
|     shareCount      integer, | ||||
|     reference       varchar(48), | ||||
|     comment         varchar(512) | ||||
| ); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-SHARE-COUNT-CONSTRAINT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create or replace function checkSharesByMembershipUuid(forMembershipUuid UUID, newShareCount integer) | ||||
| returns boolean | ||||
| language plpgsql as $$ | ||||
| declare | ||||
|     currentShareCount integer; | ||||
|     totalShareCount integer; | ||||
| begin | ||||
|     select sum(cst.shareCount) | ||||
|     from hs_office_coopsharestransaction cst | ||||
|     where cst.membershipUuid = forMembershipUuid | ||||
|     into currentShareCount; | ||||
|     totalShareCount := currentShareCount + newShareCount; | ||||
|     if totalShareCount < 0 then | ||||
|         raise exception '[400] coop shares transaction would result in a negative number of shares'; | ||||
|     end if; | ||||
|     return true; | ||||
| end; $$; | ||||
|  | ||||
| alter table hs_office_coopsharestransaction | ||||
|     add constraint hs_office_coopshares_positive | ||||
|         check ( checkSharesByMembershipUuid(membershipUuid, shareCount) ); | ||||
|  | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_coopsharestransaction'); | ||||
| --// | ||||
| @@ -0,0 +1,120 @@ | ||||
| ### rbac coopSharesTransaction | ||||
|  | ||||
| This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. | ||||
|  | ||||
| ```mermaid | ||||
| %%{init:{'flowchart':{'htmlLabels':false}}}%% | ||||
| flowchart TB | ||||
|  | ||||
| subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPerson**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.holderPerson:roles[ ] | ||||
|         style membership.partnerRel.holderPerson:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.holderPerson:OWNER[[membership.partnerRel.holderPerson:OWNER]] | ||||
|         role:membership.partnerRel.holderPerson:ADMIN[[membership.partnerRel.holderPerson:ADMIN]] | ||||
|         role:membership.partnerRel.holderPerson:REFERRER[[membership.partnerRel.holderPerson:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel.anchorPerson["`**membership.partnerRel.anchorPerson**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.anchorPerson:roles[ ] | ||||
|         style membership.partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.anchorPerson:OWNER[[membership.partnerRel.anchorPerson:OWNER]] | ||||
|         role:membership.partnerRel.anchorPerson:ADMIN[[membership.partnerRel.anchorPerson:ADMIN]] | ||||
|         role:membership.partnerRel.anchorPerson:REFERRER[[membership.partnerRel.anchorPerson:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph coopSharesTransaction["`**coopSharesTransaction**`"] | ||||
|     direction TB | ||||
|     style coopSharesTransaction fill:#dd4901,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph coopSharesTransaction:permissions[ ] | ||||
|         style coopSharesTransaction:permissions fill:#dd4901,stroke:white | ||||
|  | ||||
|         perm:coopSharesTransaction:INSERT{{coopSharesTransaction:INSERT}} | ||||
|         perm:coopSharesTransaction:UPDATE{{coopSharesTransaction:UPDATE}} | ||||
|         perm:coopSharesTransaction:SELECT{{coopSharesTransaction:SELECT}} | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership["`**membership**`"] | ||||
|     direction TB | ||||
|     style membership fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership:roles[ ] | ||||
|         style membership:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership:OWNER[[membership:OWNER]] | ||||
|         role:membership:ADMIN[[membership:ADMIN]] | ||||
|         role:membership:AGENT[[membership:AGENT]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel["`**membership.partnerRel**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel:roles[ ] | ||||
|         style membership.partnerRel:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel:OWNER[[membership.partnerRel:OWNER]] | ||||
|         role:membership.partnerRel:ADMIN[[membership.partnerRel:ADMIN]] | ||||
|         role:membership.partnerRel:AGENT[[membership.partnerRel:AGENT]] | ||||
|         role:membership.partnerRel:TENANT[[membership.partnerRel:TENANT]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel.contact["`**membership.partnerRel.contact**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.contact:roles[ ] | ||||
|         style membership.partnerRel.contact:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.contact:OWNER[[membership.partnerRel.contact:OWNER]] | ||||
|         role:membership.partnerRel.contact:ADMIN[[membership.partnerRel.contact:ADMIN]] | ||||
|         role:membership.partnerRel.contact:REFERRER[[membership.partnerRel.contact:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| %% granting roles to roles | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.anchorPerson:OWNER | ||||
| role:membership.partnerRel.anchorPerson:OWNER -.-> role:membership.partnerRel.anchorPerson:ADMIN | ||||
| role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel.anchorPerson:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.holderPerson:OWNER | ||||
| role:membership.partnerRel.holderPerson:OWNER -.-> role:membership.partnerRel.holderPerson:ADMIN | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel.holderPerson:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.contact:OWNER | ||||
| role:membership.partnerRel.contact:OWNER -.-> role:membership.partnerRel.contact:ADMIN | ||||
| role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel:OWNER | ||||
| role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN | ||||
| role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:ADMIN | ||||
| role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT | ||||
| role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER | ||||
| role:membership:OWNER -.-> role:membership:ADMIN | ||||
| role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN | ||||
| role:membership:ADMIN -.-> role:membership:AGENT | ||||
| role:membership.partnerRel:AGENT -.-> role:membership:AGENT | ||||
| role:membership:AGENT -.-> role:membership.partnerRel:TENANT | ||||
|  | ||||
| %% granting permissions to roles | ||||
| role:membership:ADMIN ==> perm:coopSharesTransaction:INSERT | ||||
| role:membership:ADMIN ==> perm:coopSharesTransaction:UPDATE | ||||
| role:membership:AGENT ==> perm:coopSharesTransaction:SELECT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,151 @@ | ||||
| --liquibase formatted sql | ||||
| -- This code generated was by RbacViewPostgresGenerator, do not amend manually. | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-OBJECT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRelatedRbacObject('hs_office_coopsharestransaction'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRoleDescriptors('hsOfficeCoopSharesTransaction', 'hs_office_coopsharestransaction'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates the roles, grants and permission for the AFTER INSERT TRIGGER. | ||||
|  */ | ||||
|  | ||||
| create or replace procedure buildRbacSystemForHsOfficeCoopSharesTransaction( | ||||
|     NEW hs_office_coopsharestransaction | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
|  | ||||
| declare | ||||
|     newMembership hs_office_membership; | ||||
|  | ||||
| begin | ||||
|     call enterTriggerForObjectUuid(NEW.uuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid    INTO newMembership; | ||||
|     assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); | ||||
|  | ||||
|     call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership)); | ||||
|     call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership)); | ||||
|  | ||||
|     call leaveTriggerForObjectUuid(NEW.uuid); | ||||
| end; $$; | ||||
|  | ||||
| /* | ||||
|     AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_coopsharestransaction row. | ||||
|  */ | ||||
|  | ||||
| create or replace function insertTriggerForHsOfficeCoopSharesTransaction_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call buildRbacSystemForHsOfficeCoopSharesTransaction(NEW); | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger insertTriggerForHsOfficeCoopSharesTransaction_tg | ||||
|     after insert on hs_office_coopsharestransaction | ||||
|     for each row | ||||
| execute procedure insertTriggerForHsOfficeCoopSharesTransaction_tf(); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-INSERT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_coopsharestransaction permissions for the related hs_office_membership rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row hs_office_membership; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_coopsharestransaction permissions for the related hs_office_membership rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM hs_office_membership | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_coopsharestransaction'), | ||||
|                     hsOfficeMembershipADMIN(row)); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_coopsharestransaction INSERT permission to specified role of new hs_office_membership rows. | ||||
| */ | ||||
| create or replace function hs_office_coopsharestransaction_hs_office_membership_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_coopsharestransaction'), | ||||
|             hsOfficeMembershipADMIN(NEW)); | ||||
|     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_coopsharestransaction_hs_office_membership_insert_tg | ||||
|     after insert on hs_office_membership | ||||
|     for each row | ||||
| execute procedure hs_office_coopsharestransaction_hs_office_membership_insert_tf(); | ||||
|  | ||||
| /** | ||||
|     Checks if the user or assumed roles are allowed to insert a row to hs_office_coopsharestransaction, | ||||
|     where the check is performed by a direct role. | ||||
|  | ||||
|     A direct role is a role depending on a foreign key directly available in the NEW row. | ||||
| */ | ||||
| create or replace function hs_office_coopsharestransaction_insert_permission_missing_tf() | ||||
|     returns trigger | ||||
|     language plpgsql as $$ | ||||
| begin | ||||
|     raise exception '[403] insert into hs_office_coopsharestransaction not allowed for current subjects % (%)', | ||||
|         currentSubjects(), currentSubjectsUuids(); | ||||
| end; $$; | ||||
|  | ||||
| create trigger hs_office_coopsharestransaction_insert_permission_check_tg | ||||
|     before insert on hs_office_coopsharestransaction | ||||
|     for each row | ||||
|     when ( not hasInsertPermission(NEW.membershipUuid, 'INSERT', 'hs_office_coopsharestransaction') ) | ||||
|         execute procedure hs_office_coopsharestransaction_insert_permission_missing_tf(); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call generateRbacIdentityViewFromProjection('hs_office_coopsharestransaction', | ||||
|     $idName$ | ||||
|         reference | ||||
|     $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopsharestransaction-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_coopsharestransaction', | ||||
|     $orderBy$ | ||||
|         reference | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         comment = new.comment | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,96 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- TODO: These changesets are just for the external remote views to simulate the legacy tables. | ||||
| --  Once we don't need the external remote views anymore, create revert changesets. | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MIGRATION-mapping:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TABLE hs_office_coopsharestransaction_legacy_id | ||||
| ( | ||||
|     uuid            uuid NOT NULL REFERENCES hs_office_coopsharestransaction(uuid), | ||||
|     member_share_id  integer NOT NULL | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MIGRATION-sequence:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE SEQUENCE IF NOT EXISTS hs_office_coopsharestransaction_legacy_id_seq | ||||
|     AS integer | ||||
|     START 1000000000 | ||||
|     OWNED BY hs_office_coopsharestransaction_legacy_id.member_share_id; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MIGRATION-default:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| ALTER TABLE hs_office_coopsharestransaction_legacy_id | ||||
|     ALTER COLUMN member_share_id | ||||
|         SET DEFAULT nextVal('hs_office_coopsharestransaction_legacy_id_seq'); | ||||
|  | ||||
| --/ | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopshares-MIGRATION-insert:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CALL defineContext('schema-migration'); | ||||
| INSERT INTO hs_office_coopsharestransaction_legacy_id(uuid, member_share_id) | ||||
|     SELECT uuid, nextVal('hs_office_coopsharestransaction_legacy_id_seq') FROM hs_office_coopsharestransaction; | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopShares-MIGRATION-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function insertCoopSharesLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'INSERT' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     INSERT INTO hs_office_coopsharestransaction_legacy_id VALUES | ||||
|         (NEW.uuid, nextVal('hs_office_coopsharestransaction_legacy_id_seq')); | ||||
|  | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger createCoopSharesLegacyIdMapping | ||||
|     after insert on hs_office_coopsharestransaction | ||||
|         for each row | ||||
|             execute procedure insertCoopSharesLegacyIdMapping(); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopShares-MIGRATION-delete-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function deleteCoopSharesLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'DELETE' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_coopsharestransaction_legacy_id | ||||
|            WHERE uuid = OLD.uuid; | ||||
|  | ||||
|     return OLD; | ||||
| end; $$; | ||||
|  | ||||
| create trigger removeCoopSharesLegacyIdMapping | ||||
|     before delete on hs_office_coopsharestransaction | ||||
|         for each row | ||||
|             execute procedure deleteCoopSharesLegacyIdMapping(); | ||||
| --/ | ||||
| @@ -0,0 +1,52 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopSharesTransaction-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single coopSharesTransaction test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeCoopSharesTransactionTestData( | ||||
|         givenPartnerNumber numeric, | ||||
|         givenMemberNumberSuffix char(2) | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask     varchar; | ||||
|     membership      hs_office_membership; | ||||
| begin | ||||
|     currentTask = 'creating coopSharesTransaction test-data ' || givenPartnerNumber::text || givenMemberNumberSuffix; | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     call defineContext(currentTask); | ||||
|     select m.uuid | ||||
|         from hs_office_membership m | ||||
|         join hs_office_partner p on p.uuid = m.partneruuid | ||||
|         where p.partnerNumber = givenPartnerNumber | ||||
|             and m.memberNumberSuffix = givenMemberNumberSuffix | ||||
|         into membership; | ||||
|  | ||||
|     raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix; | ||||
|     insert | ||||
|         into hs_office_coopsharestransaction(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment) | ||||
|         values | ||||
|             (uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-1', 'initial subscription'), | ||||
|             (uuid_generate_v4(), membership.uuid, 'CANCELLATION', '2021-09-01', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-2', 'cancelling some'), | ||||
|             (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some adjustment'); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopSharesTransaction-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeCoopSharesTransactionTestData(10001, '01'); | ||||
|         call createHsOfficeCoopSharesTransactionTestData(10002, '02'); | ||||
|         call createHsOfficeCoopSharesTransactionTestData(10003, '03'); | ||||
|     end; | ||||
| $$; | ||||
| @@ -0,0 +1,63 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MAIN-TABLE:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TYPE HsOfficeCoopAssetsTransactionType AS ENUM ('ADJUSTMENT', | ||||
|                                                        'DEPOSIT', | ||||
|                                                        'DISBURSAL', | ||||
|                                                        'TRANSFER', | ||||
|                                                        'ADOPTION', | ||||
|                                                        'CLEARING', | ||||
|                                                        'LOSS', | ||||
|                                                        'LIMITATION'); | ||||
|  | ||||
| CREATE CAST (character varying as HsOfficeCoopAssetsTransactionType) WITH INOUT AS IMPLICIT; | ||||
|  | ||||
| create table if not exists hs_office_coopassetstransaction | ||||
| ( | ||||
|     uuid            uuid unique references RbacObject (uuid) initially deferred, | ||||
|     membershipUuid  uuid not null references hs_office_membership(uuid), | ||||
|     transactionType HsOfficeCoopAssetsTransactionType not null, | ||||
|     valueDate       date not null, | ||||
|     assetValue      money, | ||||
|     reference       varchar(48), | ||||
|     comment         varchar(512) | ||||
| ); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-ASSET-VALUE-CONSTRAINT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| create or replace function checkAssetsByMembershipUuid(forMembershipUuid UUID, newAssetValue money) | ||||
| returns boolean | ||||
| language plpgsql as $$ | ||||
| declare | ||||
|     currentAssetValue money; | ||||
|     totalAssetValue money; | ||||
| begin | ||||
|     select sum(cat.assetValue) | ||||
|     from hs_office_coopassetstransaction cat | ||||
|     where cat.membershipUuid = forMembershipUuid | ||||
|     into currentAssetValue; | ||||
|     totalAssetValue := currentAssetValue + newAssetValue; | ||||
|     if totalAssetValue::numeric < 0 then | ||||
|         raise exception '[400] coop assets transaction would result in a negative balance of assets'; | ||||
|     end if; | ||||
|     return true; | ||||
| end; $$; | ||||
|  | ||||
| alter table hs_office_coopassetstransaction | ||||
|     add constraint hs_office_coopassets_positive | ||||
|         check ( checkAssetsByMembershipUuid(membershipUuid, assetValue) ); | ||||
|  | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MAIN-TABLE-JOURNAL:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call create_journal('hs_office_coopassetstransaction'); | ||||
| --// | ||||
| @@ -0,0 +1,120 @@ | ||||
| ### rbac coopAssetsTransaction | ||||
|  | ||||
| This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. | ||||
|  | ||||
| ```mermaid | ||||
| %%{init:{'flowchart':{'htmlLabels':false}}}%% | ||||
| flowchart TB | ||||
|  | ||||
| subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPerson**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.holderPerson:roles[ ] | ||||
|         style membership.partnerRel.holderPerson:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.holderPerson:OWNER[[membership.partnerRel.holderPerson:OWNER]] | ||||
|         role:membership.partnerRel.holderPerson:ADMIN[[membership.partnerRel.holderPerson:ADMIN]] | ||||
|         role:membership.partnerRel.holderPerson:REFERRER[[membership.partnerRel.holderPerson:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel.anchorPerson["`**membership.partnerRel.anchorPerson**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.anchorPerson:roles[ ] | ||||
|         style membership.partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.anchorPerson:OWNER[[membership.partnerRel.anchorPerson:OWNER]] | ||||
|         role:membership.partnerRel.anchorPerson:ADMIN[[membership.partnerRel.anchorPerson:ADMIN]] | ||||
|         role:membership.partnerRel.anchorPerson:REFERRER[[membership.partnerRel.anchorPerson:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph coopAssetsTransaction["`**coopAssetsTransaction**`"] | ||||
|     direction TB | ||||
|     style coopAssetsTransaction fill:#dd4901,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph coopAssetsTransaction:permissions[ ] | ||||
|         style coopAssetsTransaction:permissions fill:#dd4901,stroke:white | ||||
|  | ||||
|         perm:coopAssetsTransaction:INSERT{{coopAssetsTransaction:INSERT}} | ||||
|         perm:coopAssetsTransaction:UPDATE{{coopAssetsTransaction:UPDATE}} | ||||
|         perm:coopAssetsTransaction:SELECT{{coopAssetsTransaction:SELECT}} | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership["`**membership**`"] | ||||
|     direction TB | ||||
|     style membership fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership:roles[ ] | ||||
|         style membership:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership:OWNER[[membership:OWNER]] | ||||
|         role:membership:ADMIN[[membership:ADMIN]] | ||||
|         role:membership:AGENT[[membership:AGENT]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel["`**membership.partnerRel**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel:roles[ ] | ||||
|         style membership.partnerRel:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel:OWNER[[membership.partnerRel:OWNER]] | ||||
|         role:membership.partnerRel:ADMIN[[membership.partnerRel:ADMIN]] | ||||
|         role:membership.partnerRel:AGENT[[membership.partnerRel:AGENT]] | ||||
|         role:membership.partnerRel:TENANT[[membership.partnerRel:TENANT]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| subgraph membership.partnerRel.contact["`**membership.partnerRel.contact**`"] | ||||
|     direction TB | ||||
|     style membership.partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px | ||||
|  | ||||
|     subgraph membership.partnerRel.contact:roles[ ] | ||||
|         style membership.partnerRel.contact:roles fill:#99bcdb,stroke:white | ||||
|  | ||||
|         role:membership.partnerRel.contact:OWNER[[membership.partnerRel.contact:OWNER]] | ||||
|         role:membership.partnerRel.contact:ADMIN[[membership.partnerRel.contact:ADMIN]] | ||||
|         role:membership.partnerRel.contact:REFERRER[[membership.partnerRel.contact:REFERRER]] | ||||
|     end | ||||
| end | ||||
|  | ||||
| %% granting roles to roles | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.anchorPerson:OWNER | ||||
| role:membership.partnerRel.anchorPerson:OWNER -.-> role:membership.partnerRel.anchorPerson:ADMIN | ||||
| role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel.anchorPerson:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.holderPerson:OWNER | ||||
| role:membership.partnerRel.holderPerson:OWNER -.-> role:membership.partnerRel.holderPerson:ADMIN | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel.holderPerson:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel.contact:OWNER | ||||
| role:membership.partnerRel.contact:OWNER -.-> role:membership.partnerRel.contact:ADMIN | ||||
| role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel.contact:REFERRER | ||||
| role:global:ADMIN -.-> role:membership.partnerRel:OWNER | ||||
| role:membership.partnerRel:OWNER -.-> role:membership.partnerRel:ADMIN | ||||
| role:membership.partnerRel.anchorPerson:ADMIN -.-> role:membership.partnerRel:ADMIN | ||||
| role:membership.partnerRel:ADMIN -.-> role:membership.partnerRel:AGENT | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:AGENT | ||||
| role:membership.partnerRel:AGENT -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel.holderPerson:ADMIN -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel.contact:ADMIN -.-> role:membership.partnerRel:TENANT | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.anchorPerson:REFERRER | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.holderPerson:REFERRER | ||||
| role:membership.partnerRel:TENANT -.-> role:membership.partnerRel.contact:REFERRER | ||||
| role:membership:OWNER -.-> role:membership:ADMIN | ||||
| role:membership.partnerRel:ADMIN -.-> role:membership:ADMIN | ||||
| role:membership:ADMIN -.-> role:membership:AGENT | ||||
| role:membership.partnerRel:AGENT -.-> role:membership:AGENT | ||||
| role:membership:AGENT -.-> role:membership.partnerRel:TENANT | ||||
|  | ||||
| %% granting permissions to roles | ||||
| role:membership:ADMIN ==> perm:coopAssetsTransaction:INSERT | ||||
| role:membership:ADMIN ==> perm:coopAssetsTransaction:UPDATE | ||||
| role:membership:AGENT ==> perm:coopAssetsTransaction:SELECT | ||||
|  | ||||
| ``` | ||||
| @@ -0,0 +1,151 @@ | ||||
| --liquibase formatted sql | ||||
| -- This code generated was by RbacViewPostgresGenerator, do not amend manually. | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-OBJECT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRelatedRbacObject('hs_office_coopassetstransaction'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRoleDescriptors('hsOfficeCoopAssetsTransaction', 'hs_office_coopassetstransaction'); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates the roles, grants and permission for the AFTER INSERT TRIGGER. | ||||
|  */ | ||||
|  | ||||
| create or replace procedure buildRbacSystemForHsOfficeCoopAssetsTransaction( | ||||
|     NEW hs_office_coopassetstransaction | ||||
| ) | ||||
|     language plpgsql as $$ | ||||
|  | ||||
| declare | ||||
|     newMembership hs_office_membership; | ||||
|  | ||||
| begin | ||||
|     call enterTriggerForObjectUuid(NEW.uuid); | ||||
|  | ||||
|     SELECT * FROM hs_office_membership WHERE uuid = NEW.membershipUuid    INTO newMembership; | ||||
|     assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid); | ||||
|  | ||||
|     call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership)); | ||||
|     call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership)); | ||||
|  | ||||
|     call leaveTriggerForObjectUuid(NEW.uuid); | ||||
| end; $$; | ||||
|  | ||||
| /* | ||||
|     AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_coopassetstransaction row. | ||||
|  */ | ||||
|  | ||||
| create or replace function insertTriggerForHsOfficeCoopAssetsTransaction_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call buildRbacSystemForHsOfficeCoopAssetsTransaction(NEW); | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger insertTriggerForHsOfficeCoopAssetsTransaction_tg | ||||
|     after insert on hs_office_coopassetstransaction | ||||
|     for each row | ||||
| execute procedure insertTriggerForHsOfficeCoopAssetsTransaction_tf(); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-INSERT:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates INSERT INTO hs_office_coopassetstransaction permissions for the related hs_office_membership rows. | ||||
|  */ | ||||
| do language plpgsql $$ | ||||
|     declare | ||||
|         row hs_office_membership; | ||||
|     begin | ||||
|         call defineContext('create INSERT INTO hs_office_coopassetstransaction permissions for the related hs_office_membership rows'); | ||||
|  | ||||
|         FOR row IN SELECT * FROM hs_office_membership | ||||
|             LOOP | ||||
|                 call grantPermissionToRole( | ||||
|                     createPermission(row.uuid, 'INSERT', 'hs_office_coopassetstransaction'), | ||||
|                     hsOfficeMembershipADMIN(row)); | ||||
|             END LOOP; | ||||
|     END; | ||||
| $$; | ||||
|  | ||||
| /** | ||||
|     Adds hs_office_coopassetstransaction INSERT permission to specified role of new hs_office_membership rows. | ||||
| */ | ||||
| create or replace function hs_office_coopassetstransaction_hs_office_membership_insert_tf() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     call grantPermissionToRole( | ||||
|             createPermission(NEW.uuid, 'INSERT', 'hs_office_coopassetstransaction'), | ||||
|             hsOfficeMembershipADMIN(NEW)); | ||||
|     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_coopassetstransaction_hs_office_membership_insert_tg | ||||
|     after insert on hs_office_membership | ||||
|     for each row | ||||
| execute procedure hs_office_coopassetstransaction_hs_office_membership_insert_tf(); | ||||
|  | ||||
| /** | ||||
|     Checks if the user or assumed roles are allowed to insert a row to hs_office_coopassetstransaction, | ||||
|     where the check is performed by a direct role. | ||||
|  | ||||
|     A direct role is a role depending on a foreign key directly available in the NEW row. | ||||
| */ | ||||
| create or replace function hs_office_coopassetstransaction_insert_permission_missing_tf() | ||||
|     returns trigger | ||||
|     language plpgsql as $$ | ||||
| begin | ||||
|     raise exception '[403] insert into hs_office_coopassetstransaction not allowed for current subjects % (%)', | ||||
|         currentSubjects(), currentSubjectsUuids(); | ||||
| end; $$; | ||||
|  | ||||
| create trigger hs_office_coopassetstransaction_insert_permission_check_tg | ||||
|     before insert on hs_office_coopassetstransaction | ||||
|     for each row | ||||
|     when ( not hasInsertPermission(NEW.membershipUuid, 'INSERT', 'hs_office_coopassetstransaction') ) | ||||
|         execute procedure hs_office_coopassetstransaction_insert_permission_missing_tf(); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| call generateRbacIdentityViewFromProjection('hs_office_coopassetstransaction', | ||||
|     $idName$ | ||||
|         reference | ||||
|     $idName$); | ||||
| --// | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassetstransaction-rbac-RESTRICTED-VIEW:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| call generateRbacRestrictedView('hs_office_coopassetstransaction', | ||||
|     $orderBy$ | ||||
|         reference | ||||
|     $orderBy$, | ||||
|     $updates$ | ||||
|         comment = new.comment | ||||
|     $updates$); | ||||
| --// | ||||
|  | ||||
| @@ -0,0 +1,96 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
| -- TODO: These changesets are just for the external remote views to simulate the legacy tables. | ||||
| --  Once we don't need the external remote views anymore, create revert changesets. | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MIGRATION-mapping:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE TABLE hs_office_coopassetstransaction_legacy_id | ||||
| ( | ||||
|     uuid            uuid NOT NULL REFERENCES hs_office_coopassetstransaction(uuid), | ||||
|     member_asset_id  integer NOT NULL | ||||
| ); | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MIGRATION-sequence:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CREATE SEQUENCE IF NOT EXISTS hs_office_coopassetstransaction_legacy_id_seq | ||||
|     AS integer | ||||
|     START 1000000000 | ||||
|     OWNED BY hs_office_coopassetstransaction_legacy_id.member_asset_id; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MIGRATION-default:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| ALTER TABLE hs_office_coopassetstransaction_legacy_id | ||||
|     ALTER COLUMN member_asset_id | ||||
|         SET DEFAULT nextVal('hs_office_coopassetstransaction_legacy_id_seq'); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopassets-MIGRATION-insert:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| CALL defineContext('schema-migration'); | ||||
| INSERT INTO hs_office_coopassetstransaction_legacy_id(uuid, member_asset_id) | ||||
|     SELECT uuid, nextVal('hs_office_coopassetstransaction_legacy_id_seq') FROM hs_office_coopassetstransaction; | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopAssets-MIGRATION-insert-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function insertCoopAssetsLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'INSERT' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     INSERT INTO hs_office_coopassetstransaction_legacy_id VALUES | ||||
|         (NEW.uuid, nextVal('hs_office_coopassetstransaction_legacy_id_seq')); | ||||
|  | ||||
|     return NEW; | ||||
| end; $$; | ||||
|  | ||||
| create trigger createCoopAssetsLegacyIdMapping | ||||
|     after insert on hs_office_coopassetstransaction | ||||
|         for each row | ||||
|             execute procedure insertCoopAssetsLegacyIdMapping(); | ||||
| --/ | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopAssets-MIGRATION-delete-trigger:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
| create or replace function deleteCoopAssetsLegacyIdMapping() | ||||
|     returns trigger | ||||
|     language plpgsql | ||||
|     strict as $$ | ||||
| begin | ||||
|     if TG_OP <> 'DELETE' then | ||||
|         raise exception 'invalid usage of trigger'; | ||||
|     end if; | ||||
|  | ||||
|     DELETE FROM hs_office_coopassetstransaction_legacy_id | ||||
|            WHERE uuid = OLD.uuid; | ||||
|  | ||||
|     return OLD; | ||||
| end; $$; | ||||
|  | ||||
| create trigger removeCoopAssetsLegacyIdMapping | ||||
|     before delete on hs_office_coopassetstransaction | ||||
|         for each row | ||||
|             execute procedure deleteCoopAssetsLegacyIdMapping(); | ||||
| --/ | ||||
| @@ -0,0 +1,52 @@ | ||||
| --liquibase formatted sql | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopAssetsTransaction-TEST-DATA-GENERATOR:1 endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| /* | ||||
|     Creates a single coopAssetsTransaction test record. | ||||
|  */ | ||||
| create or replace procedure createHsOfficeCoopAssetsTransactionTestData( | ||||
|     givenPartnerNumber numeric, | ||||
|     givenMemberNumberSuffix char(2) | ||||
|     ) | ||||
|     language plpgsql as $$ | ||||
| declare | ||||
|     currentTask     varchar; | ||||
|     membership      hs_office_membership; | ||||
| begin | ||||
|     currentTask = 'creating coopAssetsTransaction test-data ' || givenPartnerNumber || givenMemberNumberSuffix; | ||||
|     execute format('set local hsadminng.currentTask to %L', currentTask); | ||||
|  | ||||
|     call defineContext(currentTask); | ||||
|     select m.uuid | ||||
|         from hs_office_membership m | ||||
|                  join hs_office_partner p on p.uuid = m.partneruuid | ||||
|         where p.partnerNumber = givenPartnerNumber | ||||
|           and m.memberNumberSuffix = givenMemberNumberSuffix | ||||
|         into membership; | ||||
|  | ||||
|     raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix; | ||||
|     insert | ||||
|         into hs_office_coopassetstransaction(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment) | ||||
|         values | ||||
|             (uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit'), | ||||
|             (uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal'), | ||||
|             (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment'); | ||||
| end; $$; | ||||
| --// | ||||
|  | ||||
|  | ||||
| -- ============================================================================ | ||||
| --changeset hs-office-coopAssetsTransaction-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--// | ||||
| -- ---------------------------------------------------------------------------- | ||||
|  | ||||
| do language plpgsql $$ | ||||
|     begin | ||||
|         call createHsOfficeCoopAssetsTransactionTestData(10001, '01'); | ||||
|         call createHsOfficeCoopAssetsTransactionTestData(10002, '02'); | ||||
|         call createHsOfficeCoopAssetsTransactionTestData(10003, '03'); | ||||
|     end; | ||||
| $$; | ||||
		Reference in New Issue
	
	Block a user