From 7f5b2358d3c8e2c1b5d2806a1bd3cc7dd8cd1862 Mon Sep 17 00:00:00 2001
From: Michael Hoennig <michael@hoennig.de>
Date: Thu, 13 Oct 2022 13:35:50 +0200
Subject: [PATCH] add SEPA-Mandate SQL changesets

---
 .../changelog/250-hs-office-sepamandate.sql   |  23 +++
 .../253-hs-office-sepamandate-rbac.md         |  71 ++++++++
 .../253-hs-office-sepamandate-rbac.sql        | 157 ++++++++++++++++++
 .../258-hs-office-sepamandate-test-data.sql   |  51 ++++++
 .../changelog/273-hs-office-debitor-rbac.md   |  43 +++++
 .../db/changelog/db.changelog-master.yaml     |   6 +
 6 files changed, 351 insertions(+)
 create mode 100644 src/main/resources/db/changelog/250-hs-office-sepamandate.sql
 create mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
 create mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
 create mode 100644 src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql

diff --git a/src/main/resources/db/changelog/250-hs-office-sepamandate.sql b/src/main/resources/db/changelog/250-hs-office-sepamandate.sql
new file mode 100644
index 00000000..144d0357
--- /dev/null
+++ b/src/main/resources/db/changelog/250-hs-office-sepamandate.sql
@@ -0,0 +1,23 @@
+--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),
+    validity            daterange not null
+);
+--//
+
+
+-- ============================================================================
+--changeset hs-office-sepamandate-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+call create_journal('hs_office_sepamandate');
+--//
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
new file mode 100644
index 00000000..78bb7751
--- /dev/null
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
@@ -0,0 +1,71 @@
+### hs_office_sepaMandate RBAC
+
+```mermaid
+flowchart TB
+
+subgraph global
+    style global fill:#eee
+    
+    role:global.admin[global.admin]    
+end
+
+subgraph hsOfficeBankAccount
+    direction TB
+    style hsOfficeBankAccount fill:#eee
+    
+    role:hsOfficeBankAccount.owner[bankAccount.owner]    
+    --> role:hsOfficeBankAccount.admin[bankAccount.admin]    
+    --> role:hsOfficeBankAccount.tenant[bankAccount.tenant]    
+    --> role:hsOfficeBankAccount.guest[bankAccount.guest]    
+end
+
+subgraph hsOfficeDebitor
+    direction TB
+    style hsOfficeDebitor fill:#eee
+    
+    role:hsOfficeDebitor.owner[debitor.admin]    
+    --> role:hsOfficeDebitor.admin[debitor.admin]    
+    --> role:hsOfficeDebitor.agent[debitor.agent]    
+    --> role:hsOfficeDebitor.tenant[debitor.tenant]    
+    --> role:hsOfficeDebitor.guest[debitor.guest]    
+end
+
+subgraph hsOfficeSepaMandate
+                    
+   role:hsOfficeSepaMandate.owner[sepaMandate.owner]
+   %% permissions
+       role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}}
+   %% incoming
+       role:global.admin ---> role:hsOfficeSepaMandate.owner
+  
+   role:hsOfficeSepaMandate.admin[sepaMandate.admin]
+   %% permissions
+       role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}}
+   %% incoming
+       role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin
+  
+   role:hsOfficeSepaMandate.agent[sepaMandate.agent]
+   %% incoming
+       role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent
+       role:hsOfficeDebitor.admin --> role:hsOfficeSepaMandate.agent
+       role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent
+   %% outgoing
+       role:hsOfficeSepaMandate.agent --> role:hsOfficeDebitor.tenant
+       role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.tenant
+  
+   role:hsOfficeSepaMandate.tenant[sepaMandate.tenant]
+   %% incoming
+       role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant
+   %% outgoing   
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeDebitor.guest
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeBankAccount.guest
+
+   role:hsOfficeSepaMandate.guest[sepaMandate.guest]
+   %% permissions
+       role:hsOfficeSepaMandate.guest -->  perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
+   %% incoming
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
+end
+
+
+```
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
new file mode 100644
index 00000000..56bbff58
--- /dev/null
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
@@ -0,0 +1,157 @@
+--liquibase formatted sql
+
+-- ============================================================================
+--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-ROLES-CREATION:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+/*
+    Creates and updates the roles and their assignments for sepaMandate entities.
+ */
+
+create or replace function hsOfficeSepaMandateRbacRolesTrigger()
+    returns trigger
+    language plpgsql
+    strict as $$
+declare
+    newHsOfficeDebitor      hs_office_debitor;
+    newHsOfficeBankAccount  hs_office_bankAccount;
+begin
+
+    select * from hs_office_debitor as p where p.uuid = NEW.debitorUuid into newHsOfficeDebitor;
+    select * from hs_office_bankAccount as c where c.uuid = NEW.bankAccountUuid into newHsOfficeBankAccount;
+
+    if TG_OP = 'INSERT' then
+
+
+        -- === ATTENTION: code generated from related Mermaid flowchart: ===
+
+
+        perform createRoleWithGrants(
+                hsOfficeSepaMandateOwner(NEW),
+                permissions => array['*'],
+                incomingSuperRoles => array[globalAdmin()]
+            );
+
+        perform createRoleWithGrants(
+                hsOfficeSepaMandateAdmin(NEW),
+                permissions => array['edit'],
+                incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
+                outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
+            );
+
+        perform createRoleWithGrants(
+                hsOfficeSepaMandateAgent(NEW),
+                incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW), hsOfficeDebitorAdmin(newHsOfficeDebitor), hsOfficeBankAccountAdmin(newHsOfficeBankAccount)],
+                outgoingSubRoles => array[hsOfficeDebitorTenant(newHsOfficeDebitor)]
+            );
+
+        perform createRoleWithGrants(
+                hsOfficeSepaMandateTenant(NEW),
+                incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)],
+                outgoingSubRoles => array[hsOfficeDebitorGuest(newHsOfficeDebitor), hsOfficeBankAccountGuest(newHsOfficeBankAccount)]
+            );
+
+        perform createRoleWithGrants(
+                hsOfficeSepaMandateGuest(NEW),
+                permissions => array['view'],
+                incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
+            );
+
+        -- === END of code generated from Mermaid flowchart. ===
+
+
+    else
+        raise exception 'invalid usage of TRIGGER';
+    end if;
+
+    return NEW;
+end; $$;
+
+/*
+    An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ */
+create trigger createRbacRolesForHsOfficeSepaMandate_Trigger
+    after insert
+    on hs_office_sepaMandate
+    for each row
+execute procedure hsOfficeSepaMandateRbacRolesTrigger();
+--//
+
+
+-- ============================================================================
+--changeset hs-office-sepaMandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacIdentityView('hs_office_sepaMandate', idNameExpression => 'target.reference');
+--//
+
+
+-- ============================================================================
+--changeset hs-office-sepaMandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('hs_office_sepaMandate',
+    orderby => 'target.reference',
+    columnUpdates => $updates$
+        validity = new.validity
+    $updates$);
+--//
+
+
+-- ============================================================================
+--changeset hs-office-sepaMandate-rbac-NEW-SepaMandate:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+/*
+    Creates a global permission for new-sepaMandate and assigns it to the hostsharing admins role.
+ */
+do language plpgsql $$
+    declare
+        addCustomerPermissions uuid[];
+        globalObjectUuid       uuid;
+        globalAdminRoleUuid    uuid ;
+    begin
+        call defineContext('granting global new-sepaMandate permission to global admin role', null, null, null);
+
+        globalAdminRoleUuid := findRoleId(globalAdmin());
+        globalObjectUuid := (select uuid from global);
+        addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-sepamandate']);
+        call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
+    end;
+$$;
+
+/**
+    Used by the trigger to prevent the add-customer to current user respectively assumed roles.
+ */
+create or replace function addHsOfficeSepaMandateNotAllowedForCurrentSubjects()
+    returns trigger
+    language PLPGSQL
+as $$
+begin
+    raise exception '[403] new-sepaMandate not permitted for %',
+        array_to_string(currentSubjects(), ';', 'null');
+end; $$;
+
+/**
+    Checks if the user or assumed roles are allowed to create a new customer.
+ */
+create trigger hs_office_sepaMandate_insert_trigger
+    before insert
+    on hs_office_sepaMandate
+    for each row
+    -- TODO.spec: who is allowed to create new sepaMandates
+    when ( not hasAssumedRole() )
+execute procedure addHsOfficeSepaMandateNotAllowedForCurrentSubjects();
+--//
+
diff --git a/src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql b/src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql
new file mode 100644
index 00000000..7af102a4
--- /dev/null
+++ b/src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql
@@ -0,0 +1,51 @@
+--liquibase formatted sql
+
+
+-- ============================================================================
+--changeset hs-office-sepaMandate-TEST-DATA-GENERATOR:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+/*
+    Creates a single sepaMandate test record.
+ */
+create or replace procedure createHsOfficeSepaMandateTestData( tradeNameAndHolderName varchar )
+    language plpgsql as $$
+declare
+    currentTask         varchar;
+    idName              varchar;
+    relatedDebitor      hs_office_debitor;
+    relatedBankAccount  hs_office_bankAccount;
+begin
+    idName := cleanIdentifier( tradeNameAndHolderName);
+    currentTask := 'creating SEPA-mandate test-data ' || idName;
+    call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+    execute format('set local hsadminng.currentTask to %L', currentTask);
+
+    select debitor.* from hs_office_debitor debitor
+                      join hs_office_partner parter on parter.uuid = debitor.partnerUuid
+                      join hs_office_person person on person.uuid = parter.personUuid
+                     where person.tradeName = tradeNameAndHolderName into relatedDebitor;
+    select c.* from hs_office_bankAccount c where c.holder = tradeNameAndHolderName into relatedBankAccount;
+
+    raise notice 'creating test SEPA-mandate: %', idName;
+    raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
+    raise notice '- using bankAccount (%): %', relatedBankAccount.uuid, relatedBankAccount;
+    insert
+        into hs_office_sepaMandate (uuid, debitoruuid, bankAccountuuid, reference, validity)
+        values (uuid_generate_v4(), relatedDebitor.uuid, relatedBankAccount.uuid, 'ref'||idName, daterange('20221001' , '20261231', '[]'));
+end; $$;
+--//
+
+
+-- ============================================================================
+--changeset hs-office-sepaMandate-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+do language plpgsql $$
+    begin
+        call createHsOfficeSepaMandateTestData('First GmbH');
+        call createHsOfficeSepaMandateTestData('Second e.K.');
+        call createHsOfficeSepaMandateTestData('Third OHG');
+    end;
+$$;
+--//
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md
index ab738860..55e2459a 100644
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md
@@ -12,6 +12,8 @@ end
 subgraph office
     style office fill:#eee
     
+    subgraph sepa
+    
     subgraph bankaccount
         style bankaccount fill: #e9f7ef
         
@@ -40,6 +42,11 @@ subgraph office
         %% incoming
             role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
     end
+    
+    subgraph hsOfficeSepaMandate
+    end
+    
+    end
    
     subgraph contact
         style contact fill: #e9f7ef
@@ -186,6 +193,42 @@ subgraph office
     
 end
 
+subgraph hsOfficeSepaMandate
+                    
+   role:hsOfficeSepaMandate.owner[sepaMandate.owner]
+   %% permissions
+       role:hsOfficeSepaMandate.owner --> perm:hsOfficeSepaMandate.*{{sepaMandate.*}}
+   %% incoming
+       role:global.admin ---> role:hsOfficeSepaMandate.owner
+  
+   role:hsOfficeSepaMandate.admin[sepaMandate.admin]
+   %% permissions
+       role:hsOfficeSepaMandate.admin --> perm:hsOfficeSepaMandate.edit{{sepaMandate.edit}}
+   %% incoming
+       role:hsOfficeSepaMandate.owner ---> role:hsOfficeSepaMandate.admin
+  
+   role:hsOfficeSepaMandate.agent[sepaMandate.agent]
+   %% incoming
+       role:hsOfficeSepaMandate.admin ---> role:hsOfficeSepaMandate.agent
+       role:hsOfficeDebitor.admin --> role:hsOfficeSepaMandate.agent
+       role:hsOfficeBankAccount.admin --> role:hsOfficeSepaMandate.agent
+   %% outgoing
+       role:hsOfficeSepaMandate.agent --> role:hsOfficeDebitor.tenant
+       role:hsOfficeSepaMandate.admin --> role:hsOfficeBankAccount.tenant
+  
+   role:hsOfficeSepaMandate.tenant[sepaMandate.tenant]
+   %% incoming
+       role:hsOfficeSepaMandate.agent --> role:hsOfficeSepaMandate.tenant
+   %% outgoing   
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeDebitor.guest
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeBankAccount.guest
+
+   role:hsOfficeSepaMandate.guest[sepaMandate.guest]
+   %% permissions
+       role:hsOfficeSepaMandate.guest -->  perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
+   %% incoming
+       role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
+end
 
 subgraph hosting
     style hosting fill:#eee
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 376c4609..4d041f5c 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -85,3 +85,9 @@ databaseChangeLog:
         file: db/changelog/273-hs-office-debitor-rbac.sql
     - include:
         file: db/changelog/278-hs-office-debitor-test-data.sql
+    - include:
+        file: db/changelog/250-hs-office-sepamandate.sql
+    - include:
+        file: db/changelog/253-hs-office-sepamandate-rbac.sql
+    - include:
+        file: db/changelog/258-hs-office-sepamandate-test-data.sql