1
0

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:
Michael Hoennig
2024-04-02 12:29:31 +02:00
parent 7f418c12a1
commit 87af20a3a1
94 changed files with 82 additions and 79 deletions

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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();
--/

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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();
--/

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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;
$$;

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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();
--/

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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;
$$;
--//

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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();
--/

View File

@ -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;
$$;

View File

@ -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');
--//

View File

@ -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
```

View File

@ -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$);
--//

View File

@ -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();
--/

View File

@ -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;
$$;