From 187c0db8e24906f240b7061bb83e60a45d45796e Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Mon, 11 Mar 2024 12:30:43 +0100
Subject: [PATCH 01/12] RBAC Diagram+PostgreSQL Generator and view->SELECT etc.
refactoring (#21)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/21
Reviewed-by: Timotheus Pokorra
---
doc/rbac.md | 148 ++--
sql/rbac-tests.sql | 6 +-
sql/rbac-view-option-experiments.sql | 10 +-
.../errors/ReferenceNotFoundException.java | 4 +-
.../HsOfficeBankAccountEntity.java | 27 +
.../office/contact/HsOfficeContactEntity.java | 30 +-
.../office/debitor/HsOfficeDebitorEntity.java | 80 +-
.../partner/HsOfficePartnerController.java | 4 +-
.../partner/HsOfficePartnerDetailsEntity.java | 50 +-
.../office/partner/HsOfficePartnerEntity.java | 42 +
.../office/person/HsOfficePersonEntity.java | 30 +
.../HsOfficeRelationshipEntity.java | 60 +-
.../HsOfficeSepaMandateEntity.java | 41 +
.../hsadminng/persistence/HasUuid.java | 6 +-
.../rbac/rbacdef/InsertTriggerGenerator.java | 165 ++++
.../rbacdef/PostgresTriggerReference.java | 5 +
.../rbacdef/RbacIdentityViewGenerator.java | 45 +
.../rbac/rbacdef/RbacObjectGenerator.java | 27 +
.../rbacdef/RbacRestrictedViewGenerator.java | 41 +
.../rbacdef/RbacRoleDescriptorsGenerator.java | 30 +
.../hsadminng/rbac/rbacdef/RbacView.java | 830 ++++++++++++++++++
.../RbacViewMermaidFlowchartGenerator.java | 164 ++++
.../rbacdef/RbacViewPostgresGenerator.java | 52 ++
.../RolesGrantsAndPermissionsGenerator.java | 507 +++++++++++
.../hsadminng/rbac/rbacdef/StringWriter.java | 111 +++
.../hsadminng/rbac/rbacdef/package-info.java | 5 +
.../rbac/rbacgrant/RawRbacGrantEntity.java | 9 +-
.../rbacgrant/RawRbacGrantRepository.java | 4 +
.../rbacgrant/RbacGrantsDiagramService.java | 206 +++++
.../hsadminng/rbac/rbacobject/RbacObject.java | 8 +
.../rbac/rbacuser/RbacUserPermission.java | 2 +-
.../test/cust/TestCustomerController.java | 6 +-
.../test/cust/TestCustomerEntity.java | 37 +-
.../hsadminng/test/dom/TestDomainEntity.java | 73 ++
.../hsadminng/test/pac/TestPackageEntity.java | 42 +-
.../resources/db/changelog/010-context.sql | 13 +-
.../resources/db/changelog/020-audit-log.sql | 4 +-
.../resources/db/changelog/050-rbac-base.sql | 196 +++--
.../db/changelog/051-rbac-user-grant.sql | 36 +-
.../resources/db/changelog/055-rbac-views.sql | 25 +-
.../db/changelog/057-rbac-role-builder.sql | 39 +-
.../db/changelog/058-rbac-generators.sql | 57 +-
.../db/changelog/080-rbac-global.sql | 17 +-
.../db/changelog/113-test-customer-rbac.md | 43 +
.../db/changelog/113-test-customer-rbac.sql | 152 ++--
.../changelog/118-test-customer-test-data.sql | 11 +
.../db/changelog/123-test-package-rbac.md | 59 ++
.../db/changelog/123-test-package-rbac.sql | 235 +++--
.../changelog/128-test-package-test-data.sql | 4 +-
.../db/changelog/133-test-domain-rbac.md | 88 ++
.../db/changelog/133-test-domain-rbac.sql | 260 ++++--
.../changelog/203-hs-office-contact-rbac.sql | 8 +-
.../changelog/213-hs-office-person-rbac.sql | 10 +-
.../223-hs-office-relationship-rbac.md | 148 ----
.../223-hs-office-relationship-rbac.sql | 8 +-
.../changelog/233-hs-office-partner-rbac.sql | 20 +-
.../234-hs-office-partner-details-rbac.sql | 7 +-
.../243-hs-office-bankaccount-rbac.md | 4 +-
.../243-hs-office-bankaccount-rbac.sql | 6 +-
.../253-hs-office-sepamandate-rbac.sql | 8 +-
.../changelog/273-hs-office-debitor-rbac.sql | 8 +-
.../303-hs-office-membership-rbac.sql | 8 +-
.../313-hs-office-coopshares-rbac.sql | 7 +-
.../323-hs-office-coopassets-rbac.sql | 7 +-
.../hsadminng/arch/ArchitectureTest.java | 8 +-
.../hsadminng/context/ContextBasedTest.java | 23 +
...eBankAccountRepositoryIntegrationTest.java | 4 +-
...fficeContactRepositoryIntegrationTest.java | 6 +-
...sTransactionRepositoryIntegrationTest.java | 2 +-
...sTransactionRepositoryIntegrationTest.java | 2 +-
...OfficeDebitorControllerAcceptanceTest.java | 3 +-
...fficeDebitorRepositoryIntegrationTest.java | 9 +-
...ceMembershipRepositoryIntegrationTest.java | 6 +-
.../hs/office/migration/ImportOfficeData.java | 6 +-
...fficePartnerRepositoryIntegrationTest.java | 19 +-
...OfficePersonRepositoryIntegrationTest.java | 6 +-
...RelationshipRepositoryIntegrationTest.java | 6 +-
...eSepaMandateRepositoryIntegrationTest.java | 6 +-
.../test/ContextBasedTestWithCleanup.java | 5 +-
.../RbacGrantControllerAcceptanceTest.java | 8 +-
.../RbacGrantRepositoryIntegrationTest.java | 6 +-
...acGrantsDiagramServiceIntegrationTest.java | 103 +++
.../rbac/rbacrole/RawRbacObjectEntity.java | 31 +
.../rbacrole/RawRbacObjectRepository.java | 11 +
.../RbacUserControllerAcceptanceTest.java | 38 +-
.../RbacUserRepositoryIntegrationTest.java | 175 ++--
.../TestCustomerControllerAcceptanceTest.java | 6 +-
.../test/cust/TestCustomerEntityUnitTest.java | 52 ++
...TestCustomerRepositoryIntegrationTest.java | 18 +-
.../test/pac/TestPackageEntityUnitTest.java | 68 ++
.../TestPackageRepositoryIntegrationTest.java | 15 +-
91 files changed, 4181 insertions(+), 856 deletions(-)
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/PostgresTriggerReference.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacObjectGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRoleDescriptorsGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/package-info.java
rename src/{test => main}/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java (89%)
rename src/{test => main}/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java (67%)
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/rbac/rbacobject/RbacObject.java
create mode 100644 src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
create mode 100644 src/main/resources/db/changelog/113-test-customer-rbac.md
create mode 100644 src/main/resources/db/changelog/123-test-package-rbac.md
create mode 100644 src/main/resources/db/changelog/133-test-domain-rbac.md
create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectEntity.java
create mode 100644 src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectRepository.java
create mode 100644 src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
create mode 100644 src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
diff --git a/doc/rbac.md b/doc/rbac.md
index 06a6ee7e..9aa4b024 100644
--- a/doc/rbac.md
+++ b/doc/rbac.md
@@ -11,7 +11,7 @@ Our implementation is based on Role-Based-Access-Management (RBAC) in conjunctio
As far as possible, we are using the same terms as defined in the RBAC standard, for our function names though, we chose more expressive names.
In RBAC, subjects can be assigned to roles, roles can be hierarchical and eventually have assigned permissions.
-A permission allows a specific operation (e.g. view or edit) on a specific (business-) object.
+A permission allows a specific operation (e.g. SELECT or UPDATE) on a specific (business-) object.
You can find the entity structure as a UML class diagram as follows:
@@ -101,13 +101,12 @@ package RBAC {
RbacPermission *-- RbacObject
enum RbacOperation {
- add-package
- add-domain
- add-domain
+ INSERT:package
+ INSERT:domain
...
- view
- edit
- delete
+ SELECT
+ UPDATE
+ DELETE
}
entity RbacObject {
@@ -172,11 +171,10 @@ An *RbacPermission* allows a specific *RbacOperation* on a specific *RbacObject*
An *RbacOperation* determines, what an *RbacPermission* allows to do.
It can be one of:
-- **'add-...'** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package"
-- **'view'** - permits reading the contents of the object specified by the permission
-- **'edit'** - change the contents of the object specified by the permission
-- **'delete'** - delete the object specified by the permission
-- **'\*'**
+- **'INSERT'** - permits inserting new rows related to the row, to which the permission belongs, in the table which is specified an extra column, includes 'SELECT'
+- **'SELECT'** - permits selecting the row specified by the permission, is included in all other permissions
+- **'UPDATE'** - permits updating (only the updatable columns of) the row specified by the permission, includes 'SELECT'
+- **'DELETE'** - permits deleting the row specified by the permission, includes 'SELECT'
This list is extensible according to the needs of the access rule system.
@@ -212,7 +210,7 @@ E.g. for a new *customer* it would be granted to 'administrators' and for a new
Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it.
-In most cases, the permissions to other operations than 'delete' are granted through the 'admin' role.
+In most cases, the permissions to other operations than 'DELETE' are granted through the 'admin' role.
By this, all roles ob sub-objects, which are assigned to the 'admin' role, are also granted to the 'owner'.
#### admin
@@ -220,14 +218,14 @@ By this, all roles ob sub-objects, which are assigned to the 'admin' role, are a
The admin-role is granted to a role of those subjects who manage the business object.
E.g. a 'package' is manged by the admin of the customer.
-Whoever has the admin-role assigned, can usually edit the related business-object but not deleting (or deactivating) it.
+Whoever has the admin-role assigned, can usually update the related business-object but not delete (or deactivating) it.
-The admin-role also comprises lesser roles, through which the view-permission is granted.
+The admin-role also comprises lesser roles, through which the SELECT-permission is granted.
#### agent
The agent-role is not used in the examples of this document, because it's for more complex cases.
-It's usually granted to those roles and users who represent the related business-object, but are not allowed to edit it.
+It's usually granted to those roles and users who represent the related business-object, but are not allowed to update it.
Other than the tenant-role, it usually offers broader visibility of sub-business-objects (joined entities).
E.g. a package-admin is allowed to see the related debitor-business-object,
@@ -235,19 +233,19 @@ but not its banking data.
#### tenant
-The tenant-role is granted to everybody who needs to be able to view the business-object and (probably some) related business-objects.
+The tenant-role is granted to everybody who needs to be able to select the business-object and (probably some) related business-objects.
Usually all owners, admins and tenants of sub-objects get this role granted.
-Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get view permission.
+Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get SELECT permission.
#### guest
Like the agent-role, the guest-role too is not used in the examples of this document, because it's for more complex cases.
-If the guest-role exists, the view-permission is granted to it, instead of to the tenant-role.
+If the guest-role exists, the SELECT-permission is granted to it, instead of to the tenant-role.
Other than the tenant-role, the guest-roles does never grant any roles of related objects.
-Also, if the guest-role exists, the tenant-role receives the view-permission through the guest-role.
+Also, if the guest-role exists, the tenant-role receives the SELECT-permission through the guest-role.
### Referenced Business Objects and Role-Depreciation
@@ -263,7 +261,7 @@ The admin-role of one object could be granted visibility to another object throu
But not in all cases role-depreciation takes place.
E.g. often a tenant-role is granted another tenant-role,
-because it should be again allowed to view sub-objects.
+because it should be again allowed to select sub-objects.
The same for the agent-role, often it is granted another agent-role.
@@ -297,14 +295,14 @@ package RbacRoles {
RbacUsers -[hidden]> RbacRoles
package RbacPermissions {
- object PermCustXyz_View
- object PermCustXyz_Edit
- object PermCustXyz_Delete
- object PermCustXyz_AddPackage
- object PermPackXyz00_View
- object PermPackXyz00_Edit
- object PermPackXyz00_Delete
- object PermPackXyz00_AddUser
+ object PermCustXyz_SELECT
+ object PermCustXyz_UPDATE
+ object PermCustXyz_DELETE
+ object PermCustXyz_INSERT:Package
+ object PermPackXyz00_SELECT
+ object PermPackXyz00_EDIT
+ object PermPackXyz00_DELETE
+ object PermPackXyz00_INSERT:USER
}
RbacRoles -[hidden]> RbacPermissions
@@ -322,23 +320,23 @@ RoleAdministrators o..> RoleCustXyz_Owner
RoleCustXyz_Owner o-> RoleCustXyz_Admin
RoleCustXyz_Admin o-> RolePackXyz00_Owner
-RoleCustXyz_Owner o--> PermCustXyz_Edit
-RoleCustXyz_Owner o--> PermCustXyz_Delete
-RoleCustXyz_Admin o--> PermCustXyz_View
-RoleCustXyz_Admin o--> PermCustXyz_AddPackage
-RolePackXyz00_Owner o--> PermPackXyz00_View
-RolePackXyz00_Owner o--> PermPackXyz00_Edit
-RolePackXyz00_Owner o--> PermPackXyz00_Delete
-RolePackXyz00_Owner o--> PermPackXyz00_AddUser
+RoleCustXyz_Owner o--> PermCustXyz_UPDATE
+RoleCustXyz_Owner o--> PermCustXyz_DELETE
+RoleCustXyz_Admin o--> PermCustXyz_SELECT
+RoleCustXyz_Admin o--> PermCustXyz_INSERT:Package
+RolePackXyz00_Owner o--> PermPackXyz00_SELECT
+RolePackXyz00_Owner o--> PermPackXyz00_UPDATE
+RolePackXyz00_Owner o--> PermPackXyz00_DELETE
+RolePackXyz00_Owner o--> PermPackXyz00_INSERT:User
-PermCustXyz_View o--> CustXyz
-PermCustXyz_Edit o--> CustXyz
-PermCustXyz_Delete o--> CustXyz
-PermCustXyz_AddPackage o--> CustXyz
-PermPackXyz00_View o--> PackXyz00
-PermPackXyz00_Edit o--> PackXyz00
-PermPackXyz00_Delete o--> PackXyz00
-PermPackXyz00_AddUser o--> PackXyz00
+PermCustXyz_SELECT o--> CustXyz
+PermCustXyz_UPDATE o--> CustXyz
+PermCustXyz_DELETE o--> CustXyz
+PermCustXyz_INSERT:Package o--> CustXyz
+PermPackXyz00_SELECT o--> PackXyz00
+PermPackXyz00_UPDATE o--> PackXyz00
+PermPackXyz00_DELETE o--> PackXyz00
+PermPackXyz00_INSERT:User o--> PackXyz00
@enduml
```
@@ -353,12 +351,12 @@ To support the RBAC system, for each business-object-table, some more artifacts
Not yet implemented, but planned are these actions:
-- an `ON DELETE ... DO INSTEAD` rule to allow `SQL DELETE` if applicable for the business-object-table and the user has 'delete' permission,
-- an `ON UPDATE ... DO INSTEAD` rule to allow `SQL UPDATE` if the user has 'edit' right,
-- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has 'add-..' right to the parent-business-object.
+- an `ON DELETE ... DO INSTEAD` rule to allow `SQL DELETE` if applicable for the business-object-table and the user has 'DELETE' permission,
+- an `ON UPDATE ... DO INSTEAD` rule to allow `SQL UPDATE` if the user has 'UPDATE' right,
+- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has the 'INSERT' right for the parent-business-object.
The restricted view takes the current user from a session property and applies the hierarchy of its roles all the way down to the permissions related to the respective business-object-table.
-This way, each user can only view the data they have 'view'-permission for, only create those they have 'add-...'-permission, only update those they have 'edit'- and only delete those they have 'delete'-permission to.
+This way, each user can only select the data they have 'SELECT'-permission for, only create those they have 'add-...'-permission, only update those they have 'UPDATE'- and only delete those they have 'DELETE'-permission to.
### Current User
@@ -458,26 +456,26 @@ allow_mixing
entity "BObj customer#xyz" as boCustXyz
together {
- entity "Perm customer#xyz *" as permCustomerXyzAll
- permCustomerXyzAll --> boCustXyz
+ entity "Perm customer#xyz *" as permCustomerXyzDELETE
+ permCustomerXyzDELETE --> boCustXyz
- entity "Perm customer#xyz add-package" as permCustomerXyzAddPack
- permCustomerXyzAddPack --> boCustXyz
+ entity "Perm customer#xyz INSERT:package" as permCustomerXyzINSERT:package
+ permCustomerXyzINSERT:package --> boCustXyz
- entity "Perm customer#xyz view" as permCustomerXyzView
- permCustomerXyzView --> boCustXyz
+ entity "Perm customer#xyz SELECT" as permCustomerXyzSELECT
+ permCustomerXyzSELECT--> boCustXyz
}
entity "Role customer#xyz.tenant" as roleCustXyzTenant
-roleCustXyzTenant --> permCustomerXyzView
+roleCustXyzTenant --> permCustomerXyzSELECT
entity "Role customer#xyz.admin" as roleCustXyzAdmin
roleCustXyzAdmin --> roleCustXyzTenant
-roleCustXyzAdmin --> permCustomerXyzAddPack
+roleCustXyzAdmin --> permCustomerXyzINSERT:package
entity "Role customer#xyz.owner" as roleCustXyzOwner
roleCustXyzOwner ..> roleCustXyzAdmin
-roleCustXyzOwner --> permCustomerXyzAll
+roleCustXyzOwner --> permCustomerXyzDELETE
actor "Customer XYZ Admin" as actorCustXyzAdmin
actorCustXyzAdmin --> roleCustXyzAdmin
@@ -487,8 +485,6 @@ roleAdmins --> roleCustXyzOwner
actor "Any Hostmaster" as actorHostmaster
actorHostmaster --> roleAdmins
-
-
@enduml
```
@@ -527,17 +523,17 @@ allow_mixing
entity "BObj package#xyz00" as boPacXyz00
together {
- entity "Perm package#xyz00 *" as permPackageXyzAll
- permPackageXyzAll --> boPacXyz00
+ entity "Perm package#xyz00 *" as permPackageXyzDELETE
+ permPackageXyzDELETE --> boPacXyz00
- entity "Perm package#xyz00 add-domain" as permPacXyz00AddUser
- permPacXyz00AddUser --> boPacXyz00
+ entity "Perm package#xyz00 INSERT:domain" as permPacXyz00INSERT:user
+ permPacXyz00INSERT:user --> boPacXyz00
- entity "Perm package#xyz00 edit" as permPacXyz00Edit
- permPacXyz00Edit --> boPacXyz00
+ entity "Perm package#xyz00 UPDATE" as permPacXyz00UPDATE
+ permPacXyz00UPDATE --> boPacXyz00
- entity "Perm package#xyz00 view" as permPacXyz00View
- permPacXyz00View --> boPacXyz00
+ entity "Perm package#xyz00 SELECT" as permPacXyz00SELECT
+ permPacXyz00SELECT --> boPacXyz00
}
package {
@@ -552,11 +548,11 @@ package {
entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
}
-rolePacXyz00Tenant --> permPacXyz00View
+rolePacXyz00Tenant --> permPacXyz00SELECT
rolePacXyz00Tenant --> roleCustXyzTenant
rolePacXyz00Owner --> rolePacXyz00Admin
-rolePacXyz00Owner --> permPackageXyzAll
+rolePacXyz00Owner --> permPackageXyzDELETE
roleCustXyzAdmin --> rolePacXyz00Owner
roleCustXyzAdmin --> roleCustXyzTenant
@@ -564,8 +560,8 @@ roleCustXyzAdmin --> roleCustXyzTenant
roleCustXyzOwner ..> roleCustXyzAdmin
rolePacXyz00Admin --> rolePacXyz00Tenant
-rolePacXyz00Admin --> permPacXyz00AddUser
-rolePacXyz00Admin --> permPacXyz00Edit
+rolePacXyz00Admin --> permPacXyz00INSERT:user
+rolePacXyz00Admin --> permPacXyz00UPDATE
actor "Package XYZ00 Admin" as actorPacXyzAdmin
actorPacXyzAdmin -l-> rolePacXyz00Admin
@@ -624,10 +620,10 @@ Let's have a look at the two view queries:
WHERE target.uuid IN (
SELECT uuid
FROM queryAccessibleObjectUuidsOfSubjectIds(
- 'view', 'customer', currentSubjectsUuids()));
+ 'SELECT, 'customer', currentSubjectsUuids()));
This view should be automatically updatable.
-Where, for updates, we actually have to check for 'edit' instead of 'view' operation, which makes it a bit more complicated.
+Where, for updates, we actually have to check for 'UPDATE' instead of 'SELECT' operation, which makes it a bit more complicated.
With the larger dataset, the test suite initially needed over 7 seconds with this view query.
At this point the second variant was tried.
@@ -642,7 +638,7 @@ Looks like the query optimizer needed some statistics to find the best path.
SELECT DISTINCT target.*
FROM customer AS target
JOIN queryAccessibleObjectUuidsOfSubjectIds(
- 'view', 'customer', currentSubjectsUuids()) AS allowedObjId
+ 'SELECT, 'customer', currentSubjectsUuids()) AS allowedObjId
ON target.uuid = allowedObjId;
This view cannot is not updatable automatically,
@@ -688,7 +684,7 @@ Otherwise, it would not be possible to assign roles to new users.
All roles are system-defined and cannot be created or modified by any external API.
-Users can view only the roles to which they are assigned.
+Users can view only the roles to which are granted to them.
## RbacGrant
diff --git a/sql/rbac-tests.sql b/sql/rbac-tests.sql
index 4e179dee..e30ac926 100644
--- a/sql/rbac-tests.sql
+++ b/sql/rbac-tests.sql
@@ -25,7 +25,7 @@ FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
select *
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
- 'delete'));
+ 'DELETE'));
DO LANGUAGE plpgsql
$$
@@ -34,12 +34,12 @@ $$
result bool;
BEGIN
userId = findRbacUser('superuser-alex@hostsharing.net');
- result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'add-package'), userId));
+ result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId));
IF (result) THEN
RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
end if;
- result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'view'), userId));
+ result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'SELECT'), userId));
IF (NOT result) THEN
RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
end if;
diff --git a/sql/rbac-view-option-experiments.sql b/sql/rbac-view-option-experiments.sql
index d3ef736a..f6e80e10 100644
--- a/sql/rbac-view-option-experiments.sql
+++ b/sql/rbac-view-option-experiments.sql
@@ -20,7 +20,7 @@ CREATE POLICY customer_policy ON customer
TO restricted
USING (
-- id=1000
- isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid())
+ isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid())
);
SET SESSION AUTHORIZATION restricted;
@@ -35,7 +35,7 @@ SELECT * FROM customer;
CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view
DO INSTEAD
- SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid());
+ SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid());
SELECT * from cust_view LIMIT 10;
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net'));
@@ -52,7 +52,7 @@ CREATE OR REPLACE RULE "_RETURN" AS
DO INSTEAD
SELECT c.uuid, c.reference, c.prefix FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
- ON p.objectTable='test_customer' AND p.objectUuid=c.uuid AND p.op in ('*', 'view');
+ ON p.objectTable='test_customer' AND p.objectUuid=c.uuid;
GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted;
@@ -68,7 +68,7 @@ CREATE OR REPLACE VIEW cust_view AS
SELECT c.uuid, c.reference, c.prefix
FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
- ON p.objectUuid=c.uuid AND p.op in ('*', 'view');
+ ON p.objectUuid=c.uuid;
GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted;
@@ -81,7 +81,7 @@ select rr.uuid, rr.type from RbacGrants g
join RbacReference RR on g.ascendantUuid = RR.uuid
where g.descendantUuid in (
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'))
- where objectTable='test_customer' and op in ('*', 'view'));
+ where objectTable='test_customer');
call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
diff --git a/src/main/java/net/hostsharing/hsadminng/errors/ReferenceNotFoundException.java b/src/main/java/net/hostsharing/hsadminng/errors/ReferenceNotFoundException.java
index e20d1357..deeae9f8 100644
--- a/src/main/java/net/hostsharing/hsadminng/errors/ReferenceNotFoundException.java
+++ b/src/main/java/net/hostsharing/hsadminng/errors/ReferenceNotFoundException.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.errors;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import java.util.UUID;
@@ -8,7 +8,7 @@ public class ReferenceNotFoundException extends RuntimeException {
private final Class> entityClass;
private final UUID uuid;
- public ReferenceNotFoundException(final Class entityClass, final UUID uuid, final Throwable exc) {
+ public ReferenceNotFoundException(final Class entityClass, final UUID uuid, final Throwable exc) {
super(exc);
this.entityClass = entityClass;
this.uuid = uuid;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
index 4d067f68..de256ca1 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
@@ -4,6 +4,7 @@ import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@@ -11,8 +12,13 @@ import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -50,4 +56,25 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
public String toShortString() {
return holder;
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
+ .withIdentityView(SQL.projection("iban || ':' || holder"))
+ .withUpdatableColumns("holder", "iban", "bic")
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR);
+ with.incomingSuperRole(GLOBAL, ADMIN);
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.permission(UPDATE);
+ })
+ .createSubRole(REFERRER, (with) -> {
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
index 69555dc4..406b232c 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
@@ -4,13 +4,21 @@ import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -28,7 +36,6 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
.withProp(Fields.label, HsOfficeContactEntity::getLabel)
.withProp(Fields.emailAddresses, HsOfficeContactEntity::getEmailAddresses);
-
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
@@ -53,4 +60,25 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
public String toShortString() {
return label;
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("contact", HsOfficeContactEntity.class)
+ .withIdentityView(SQL.projection("label"))
+ .withUpdatableColumns("label", "postalAddress", "emailAddresses", "phoneNumbers")
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR);
+ with.incomingSuperRole(GLOBAL, ADMIN);
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.permission(UPDATE);
+ })
+ .createSubRole(REFERRER, (with) -> {
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("203-hs-office-contact-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 76480ac0..29a9452d 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -4,16 +4,25 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
+import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -78,7 +87,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private String defaultPrefix;
private String getDebitorNumberString() {
- if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null ) {
+ if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null) {
return null;
}
return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix);
@@ -97,4 +106,71 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public String toShortString() {
return DEBITOR_NUMBER_TAG + getDebitorNumberString();
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
+ .withIdentityView(SQL.query("""
+ SELECT debitor.uuid,
+ 'D-' || (SELECT partner.partnerNumber
+ FROM hs_office_partner partner
+ JOIN hs_office_relationship partnerRel
+ ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER'
+ JOIN hs_office_relationship debitorRel
+ ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
+ WHERE debitorRel.uuid = debitor.debitorRelUuid)
+ || to_char(debitorNumberSuffix, 'fm00')
+ from hs_office_debitor as debitor
+ """))
+ .withUpdatableColumns(
+ "debitorRel",
+ "billable",
+ "debitorUuid",
+ "refundBankAccountUuid",
+ "vatId",
+ "vatCountryCode",
+ "vatBusiness",
+ "vatReverseCharge",
+ "defaultPrefix" /* TODO: do we want that updatable? */)
+ .createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
+
+ .importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
+ fetchedBySql("""
+ SELECT *
+ FROM hs_office_relationship AS r
+ WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ """),
+ dependsOnColumn("debitorRelUuid"))
+ .createPermission(DELETE).grantedTo("debitorRel", OWNER)
+ .createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
+ .createPermission(SELECT).grantedTo("debitorRel", TENANT)
+
+ .importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
+ dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
+ SELECT *
+ FROM hs_office_relationship AS r
+ WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ """)
+ )
+ .toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
+ .toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
+
+ .importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
+ dependsOnColumn("partnerRelUuid"), fetchedBySql("""
+ SELECT *
+ FROM hs_office_relationship AS partnerRel
+ WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid
+ """)
+ )
+ .toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
+ .toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
+ .toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
+ .declarePlaceholderEntityAliases("partnerPerson", "operationalPerson")
+ .forExampleRole("partnerPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN)
+ .forExampleRole("operationalPerson", ADMIN).wouldBeGrantedTo("partnerRel", ADMIN)
+ .forExampleRole("partnerRel", TENANT).wouldBeGrantedTo("partnerPerson", REFERRER);
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
index 04dcbb6a..6fdd0732 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
@@ -8,12 +8,12 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartne
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource;
-import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.mapper.Mapper;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -158,7 +158,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
return entity;
}
- private E ref(final Class entityClass, final UUID uuid) {
+ private E ref(final Class entityClass, final UUID uuid) {
try {
return em.getReference(entityClass, uuid);
} catch (final Throwable exc) {
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index 55b30148..e557f9ae 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -2,14 +2,23 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
+import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*;
+import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -55,6 +64,45 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
return registrationNumber != null ? registrationNumber
: birthName != null ? birthName
: birthday != null ? birthday.toString()
- : dateOfDeath != null ? dateOfDeath.toString() : "";
+ : dateOfDeath != null ? dateOfDeath.toString()
+ : "";
+ }
+
+
+ public static RbacView rbac() {
+ return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
+ .withIdentityView(SQL.query("""
+ SELECT partner_iv.idName || '-details'
+ 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
+ """))
+ .withUpdatableColumns(
+ "registrationOffice",
+ "registrationNumber",
+ "birthPlace",
+ "birthName",
+ "birthday",
+ "dateOfDeath")
+ .createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
+
+ .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
+ fetchedBySql("""
+ SELECT partnerRel.*
+ FROM hs_office_relationship AS partnerRel
+ JOIN hs_office_partner AS partner
+ ON partner.detailsUuid = ${ref}.uuid
+ WHERE partnerRel.uuid = partner.partnerRoleUuid
+ """),
+ dependsOnColumn("partnerRoleUuid"))
+
+ // The grants are defined in HsOfficePartnerEntity.rbac()
+ // because they have to be changed when its partnerRel changes,
+ // not when anything in partner details changes.
+ ;
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac-generated");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
index 342b601c..aa000f67 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
@@ -6,15 +6,24 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -68,4 +77,37 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
public String toShortString() {
return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("");
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("partner", HsOfficePartnerEntity.class)
+ .withIdentityView(SQL.query("""
+ SELECT partner.partnerNumber
+ || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
+ || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
+ FROM hs_office_partner AS partner
+ """))
+ .withUpdatableColumns(
+ "partnerRoleUuid",
+ "personUuid",
+ "contactUuid")
+ .createPermission(custom("new-partner")).grantedTo("global", ADMIN)
+
+ .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
+ fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
+ dependsOnColumn("partnerRelUuid"))
+ .createPermission(DELETE).grantedTo("partnerRel", ADMIN)
+ .createPermission(UPDATE).grantedTo("partnerRel", AGENT)
+ .createPermission(SELECT).grantedTo("partnerRel", TENANT)
+
+ .importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
+ fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
+ dependsOnColumn("detailsUuid"))
+ .createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
+ .createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
+ .createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT);
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("233-hs-office-partner-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
index fde3972b..fcc89dde 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
@@ -4,13 +4,21 @@ import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.apache.commons.lang3.StringUtils;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -56,4 +64,26 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
return personType + " " +
(!StringUtils.isEmpty(tradeName) ? tradeName : (familyName + ", " + givenName));
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("person", HsOfficePersonEntity.class)
+ .withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
+ .withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
+ .createRole(OWNER, (with) -> {
+ with.permission(DELETE);
+ with.owningUser(CREATOR);
+ with.incomingSuperRole(GLOBAL, ADMIN);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.permission(UPDATE);
+ })
+ .createSubRole(REFERRER, (with) -> {
+ with.permission(SELECT);
+ });
+ }
+
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java
index 704f2760..1ec9fd74 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java
@@ -3,14 +3,24 @@ package net.hostsharing.hsadminng.hs.office.relationship;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -67,4 +77,52 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
public String toShortString() {
return toShortString.apply(this);
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
+ .withIdentityView(SQL.projection("""
+ (select idName from hs_office_person_iv p where p.uuid = relAnchorUuid)
+ || '-with-' || target.relType || '-'
+ || (select idName from hs_office_person_iv p where p.uuid = relHolderUuid)
+ """))
+ .withRestrictedViewOrderBy(SQL.expression(
+ "(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)"))
+ .withUpdatableColumns("contactUuid")
+ .importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
+ dependsOnColumn("relAnchorUuid"),
+ fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid")
+ )
+ .importEntityAlias("holderPerson", HsOfficePersonEntity.class,
+ dependsOnColumn("relHolderUuid"),
+ fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid")
+ )
+ .importEntityAlias("contact", HsOfficeContactEntity.class,
+ dependsOnColumn("contactUuid"),
+ fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid")
+ )
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR);
+ with.incomingSuperRole(GLOBAL, ADMIN);
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.incomingSuperRole("anchorPerson", ADMIN);
+ with.permission(UPDATE);
+ })
+ .createSubRole(AGENT, (with) -> {
+ with.incomingSuperRole("holderPerson", ADMIN);
+ })
+ .createSubRole(TENANT, (with) -> {
+ with.incomingSuperRole("holderPerson", ADMIN);
+ with.incomingSuperRole("contact", ADMIN);
+ with.outgoingSubRole("anchorPerson", REFERRER);
+ with.outgoingSubRole("holderPerson", REFERRER);
+ with.outgoingSubRole("contact", REFERRER);
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("223-hs-office-relationship-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index baed26aa..7fcef622 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -6,16 +6,26 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
+import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;
import jakarta.persistence.*;
+import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -84,4 +94,35 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
return reference;
}
+ public static RbacView rbac() {
+ return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
+ .withIdentityView(projection("concat(tradeName, familyName, givenName)"))
+ .withUpdatableColumns("reference", "agreement", "validity")
+
+ .importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid"))
+ .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
+
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR);
+ with.incomingSuperRole(GLOBAL, ADMIN);
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.permission(UPDATE);
+ })
+ .createSubRole(AGENT, (with) -> {
+ with.outgoingSubRole("bankAccount", REFERRER);
+ with.outgoingSubRole("debitorRel", AGENT);
+ })
+ .createSubRole(REFERRER, (with) -> {
+ with.incomingSuperRole("bankAccount", ADMIN);
+ with.incomingSuperRole("debitorRel", AGENT);
+ with.outgoingSubRole("debitorRel", TENANT);
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java b/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
index 1f3ead14..03e6abf3 100644
--- a/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
+++ b/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
@@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.persistence;
-import java.util.UUID;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
-public interface HasUuid {
- UUID getUuid();
+// TODO: remove this interface, I just wanted to avoid to many changes in that PR
+public interface HasUuid extends RbacObject {
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
new file mode 100644
index 00000000..5303c27e
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
@@ -0,0 +1,165 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import java.util.Optional;
+import java.util.function.BinaryOperator;
+import java.util.stream.Stream;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+import static org.apache.commons.lang3.StringUtils.capitalize;
+import static org.apache.commons.lang3.StringUtils.uncapitalize;
+
+public class InsertTriggerGenerator {
+
+ private final RbacView rbacDef;
+ private final String liquibaseTagPrefix;
+
+ public InsertTriggerGenerator(final RbacView rbacDef, final String liqibaseTagPrefix) {
+ this.rbacDef = rbacDef;
+ this.liquibaseTagPrefix = liqibaseTagPrefix;
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ generateLiquibaseChangesetHeader(plPgSql);
+ generateGrantInsertRoleToExistingCustomers(plPgSql);
+ generateInsertPermissionGrantTrigger(plPgSql);
+ generateInsertCheckTrigger(plPgSql);
+ plPgSql.writeLn("--//");
+ }
+
+ private void generateLiquibaseChangesetHeader(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-INSERT:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix));
+ }
+
+ private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
+ getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
+ plPgSql.writeLn("""
+ /*
+ Creates INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows.
+ */
+ do language plpgsql $$
+ declare
+ row ${rawSuperTableName};
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows');
+
+ FOR row IN SELECT * FROM ${rawSuperTableName}
+ LOOP
+ roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
+ permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
+ call grantPermissionToRole(roleUuid, permissionUuid);
+ END LOOP;
+ END;
+ $$;
+ """,
+ with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
+ with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
+ with("rawSuperRoleDescriptor", toVar(superRoleDef))
+ );
+ });
+ }
+
+ private void generateInsertPermissionGrantTrigger(final StringWriter plPgSql) {
+ getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
+ plPgSql.writeLn("""
+ /**
+ Adds ${rawSubTableName} INSERT permission to specified role of new ${rawSuperTableName} rows.
+ */
+ create or replace function ${rawSubTableName}_${rawSuperTableName}_insert_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+ begin
+ call grantPermissionToRole(
+ ${rawSuperRoleDescriptor}(NEW),
+ createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
+ return NEW;
+ end; $$;
+
+ create trigger ${rawSubTableName}_${rawSuperTableName}_insert_tg
+ after insert on ${rawSuperTableName}
+ for each row
+ execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
+ """,
+ with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
+ with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
+ with("rawSuperRoleDescriptor", toVar(superRoleDef))
+ );
+ });
+ }
+
+ private void generateInsertCheckTrigger(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ /**
+ Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
+ */
+ create or replace function ${rawSubTable}_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+ begin
+ raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+ end; $$;
+ """,
+ with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
+ getOptionalInsertGrant().ifPresentOrElse(g -> {
+ plPgSql.writeLn("""
+ create trigger ${rawSubTable}_insert_permission_check_tg
+ before insert on ${rawSubTable}
+ for each row
+ when ( not hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
+ execute procedure ${rawSubTable}_insert_permission_missing_tf();
+ """,
+ with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
+ with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
+ },
+ () -> {
+ plPgSql.writeLn("""
+ create trigger ${rawSubTable}_insert_permission_check_tg
+ before insert on ${rawSubTable}
+ for each row
+ -- As there is no explicit INSERT grant specified for this table,
+ -- only global admins are allowed to insert any rows.
+ when ( not isGlobalAdmin() )
+ execute procedure ${rawSubTable}_insert_permission_missing_tf();
+ """,
+ with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
+ });
+ }
+
+ private Stream getInsertGrants() {
+ return rbacDef.getGrantDefs().stream()
+ .filter(g -> g.grantType() == PERM_TO_ROLE)
+ .filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT);
+ }
+
+ private Optional getOptionalInsertGrant() {
+ return getInsertGrants()
+ .reduce(singleton());
+ }
+
+ private Optional getOptionalInsertSuperRole() {
+ return getInsertGrants()
+ .map(RbacView.RbacGrantDefinition::getSuperRoleDef)
+ .reduce(singleton());
+ }
+
+ private static BinaryOperator singleton() {
+ return (x, y) -> {
+ throw new IllegalStateException("only a single INSERT permission grant allowed");
+ };
+ }
+
+ private static String toVar(final RbacView.RbacRoleDefinition roleDef) {
+ return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
+ }
+
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/PostgresTriggerReference.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/PostgresTriggerReference.java
new file mode 100644
index 00000000..4fb5cb61
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/PostgresTriggerReference.java
@@ -0,0 +1,5 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+public enum PostgresTriggerReference {
+ NEW, OLD
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
new file mode 100644
index 00000000..d664a83b
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
@@ -0,0 +1,45 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+
+public class RbacIdentityViewGenerator {
+ private final RbacView rbacDef;
+ private final String liquibaseTagPrefix;
+ private final String simpleEntityVarName;
+ private final String rawTableName;
+
+ public RbacIdentityViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
+ this.rbacDef = rbacDef;
+ this.liquibaseTagPrefix = liquibaseTagPrefix;
+ this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
+ this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix));
+
+ plPgSql.writeLn(
+ switch (rbacDef.getIdentityViewSqlQuery().part) {
+ case SQL_PROJECTION -> """
+ call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
+ ${identityViewSqlPart}
+ $idName$);
+ """;
+ case SQL_QUERY -> """
+ call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
+ ${identityViewSqlPart}
+ $idName$);
+ """;
+ default -> throw new IllegalStateException("illegal SQL part given");
+ },
+ with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
+ with("rawTableName", rawTableName));
+
+ plPgSql.writeLn("--//");
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacObjectGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacObjectGenerator.java
new file mode 100644
index 00000000..a7377301
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacObjectGenerator.java
@@ -0,0 +1,27 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+
+public class RbacObjectGenerator {
+
+ private final String liquibaseTagPrefix;
+ private final String rawTableName;
+
+ public RbacObjectGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
+ this.liquibaseTagPrefix = liquibaseTagPrefix;
+ this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-OBJECT:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ call generateRelatedRbacObject('${rawTableName}');
+ --//
+
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix),
+ with("rawTableName", rawTableName));
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
new file mode 100644
index 00000000..f8f6e890
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
@@ -0,0 +1,41 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+
+import static java.util.stream.Collectors.joining;
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.indented;
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+
+public class RbacRestrictedViewGenerator {
+ private final RbacView rbacDef;
+ private final String liquibaseTagPrefix;
+ private final String simpleEntityVarName;
+ private final String rawTableName;
+
+ public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
+ this.rbacDef = rbacDef;
+ this.liquibaseTagPrefix = liquibaseTagPrefix;
+ this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
+ this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ call generateRbacRestrictedView('${rawTableName}',
+ '${orderBy}',
+ $updates$
+ ${updates}
+ $updates$);
+ --//
+
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix),
+ with("orderBy", rbacDef.getOrderBySqlExpression().sql),
+ with("updates", indented(rbacDef.getUpdatableColumns().stream()
+ .map(c -> c + " = new." + c)
+ .collect(joining(",\n")), 2)),
+ with("rawTableName", rawTableName));
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRoleDescriptorsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRoleDescriptorsGenerator.java
new file mode 100644
index 00000000..dab3ab01
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRoleDescriptorsGenerator.java
@@ -0,0 +1,30 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+
+public class RbacRoleDescriptorsGenerator {
+
+ private final String liquibaseTagPrefix;
+ private final String simpleEntityVarName;
+ private final String rawTableName;
+
+ public RbacRoleDescriptorsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
+ this.liquibaseTagPrefix = liquibaseTagPrefix;
+ this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
+ this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ call generateRbacRoleDescriptors('${simpleEntityVarName}', '${rawTableName}');
+ --//
+
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix),
+ with("simpleEntityVarName", simpleEntityVarName),
+ with("rawTableName", rawTableName));
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
new file mode 100644
index 00000000..28d29365
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
@@ -0,0 +1,830 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
+import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
+import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
+import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionEntity;
+import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
+import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
+import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
+import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
+import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
+import net.hostsharing.hsadminng.test.dom.TestDomainEntity;
+import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
+
+import jakarta.persistence.Table;
+import jakarta.persistence.Version;
+import jakarta.validation.constraints.NotNull;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import static java.lang.reflect.Modifier.isStatic;
+import static java.util.Arrays.stream;
+import static java.util.Optional.ofNullable;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
+import static org.apache.commons.lang3.StringUtils.uncapitalize;
+
+@Getter
+public class RbacView {
+
+ public static final String GLOBAL = "global";
+ public static final String OUTPUT_BASEDIR = "src/main/resources/db/changelog";
+
+ private final EntityAlias rootEntityAlias;
+
+ private final Set userDefs = new LinkedHashSet<>();
+ private final Set roleDefs = new LinkedHashSet<>();
+ private final Set permDefs = new LinkedHashSet<>();
+ private final Map entityAliases = new HashMap<>() {
+
+ @Override
+ public EntityAlias put(final String key, final EntityAlias value) {
+ if (containsKey(key)) {
+ throw new IllegalArgumentException("duplicate entityAlias: " + key);
+ }
+ return super.put(key, value);
+ }
+ };
+ private final Set updatableColumns = new LinkedHashSet<>();
+ private final Set grantDefs = new LinkedHashSet<>();
+
+ private SQL identityViewSqlQuery;
+ private SQL orderBySqlExpression;
+ private EntityAlias rootEntityAliasProxy;
+ private RbacRoleDefinition previousRoleDef;
+
+ public static RbacView rbacViewFor(final String alias, final Class entityClass) {
+ return new RbacView(alias, entityClass);
+ }
+
+ RbacView(final String alias, final Class extends RbacObject> entityClass) {
+ rootEntityAlias = new EntityAlias(alias, entityClass);
+ entityAliases.put(alias, rootEntityAlias);
+ new RbacUserReference(CREATOR);
+ entityAliases.put("global", new EntityAlias("global"));
+ }
+
+ public RbacView withUpdatableColumns(final String... columnNames) {
+ Collections.addAll(updatableColumns, columnNames);
+ verifyVersionColumnExists();
+ return this;
+ }
+
+ public RbacView withIdentityView(final SQL sqlExpression) {
+ this.identityViewSqlQuery = sqlExpression;
+ return this;
+ }
+
+ public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) {
+ this.orderBySqlExpression = orderBySqlExpression;
+ return this;
+ }
+
+ public RbacView createRole(final Role role, final Consumer with) {
+ final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
+ with.accept(newRoleDef);
+ previousRoleDef = newRoleDef;
+ return this;
+ }
+
+ public RbacView createSubRole(final Role role) {
+ final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
+ findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
+ previousRoleDef = newRoleDef;
+ return this;
+ }
+
+ public RbacView createSubRole(final Role role, final Consumer with) {
+ final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
+ findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
+ with.accept(newRoleDef);
+ previousRoleDef = newRoleDef;
+ return this;
+ }
+
+ public RbacPermissionDefinition createPermission(final Permission permission) {
+ return createPermission(rootEntityAlias, permission);
+ }
+
+ public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
+ return createPermission(findEntityAlias(entityAliasName), permission);
+ }
+
+ private RbacPermissionDefinition createPermission(final EntityAlias entityAlias, final Permission permission) {
+ return new RbacPermissionDefinition(entityAlias, permission, null, true);
+ }
+
+ public RbacView declarePlaceholderEntityAliases(final String... aliasNames) {
+ for (String alias : aliasNames) {
+ entityAliases.put(alias, new EntityAlias(alias));
+ }
+ return this;
+ }
+
+ public RbacView importRootEntityAliasProxy(
+ final String aliasName,
+ final Class extends HasUuid> entityClass,
+ final SQL fetchSql,
+ final Column dependsOnColum) {
+ if (rootEntityAliasProxy != null) {
+ throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
+ }
+ rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
+ return this;
+ }
+
+ public RbacView importSubEntityAlias(
+ final String aliasName, final Class extends HasUuid> entityClass,
+ final SQL fetchSql, final Column dependsOnColum) {
+ importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
+ return this;
+ }
+
+ public RbacView importEntityAlias(
+ final String aliasName, final Class extends HasUuid> entityClass,
+ final Column dependsOnColum, final SQL fetchSql) {
+ importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
+ return this;
+ }
+
+ public RbacView importEntityAlias(
+ final String aliasName, final Class extends HasUuid> entityClass,
+ final Column dependsOnColum) {
+ importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
+ return this;
+ }
+
+ private EntityAlias importEntityAliasImpl(
+ final String aliasName, final Class extends HasUuid> entityClass,
+ final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
+ final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
+ entityAliases.put(aliasName, entityAlias);
+ try {
+ importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
+ } catch (final ReflectiveOperationException exc) {
+ throw new RuntimeException("cannot import entity: " + entityClass, exc);
+ }
+ return entityAlias;
+ }
+
+ private static RbacView rbacDefinition(final Class extends RbacObject> entityClass)
+ throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ return (RbacView) entityClass.getMethod("rbac").invoke(null);
+ }
+
+ private RbacView importAsAlias(final String aliasName, final RbacView importedRbacView, final boolean asSubEntity) {
+ final var mapper = new AliasNameMapper(importedRbacView, aliasName,
+ asSubEntity ? entityAliases.keySet() : null);
+ importedRbacView.getEntityAliases().values().stream()
+ .filter(entityAlias -> !importedRbacView.isRootEntityAlias(entityAlias))
+ .filter(entityAlias -> !entityAlias.isGlobal())
+ .filter(entityAlias -> !asSubEntity || !entityAliases.containsKey(entityAlias.aliasName))
+ .forEach(entityAlias -> {
+ final String mappedAliasName = mapper.map(entityAlias.aliasName);
+ entityAliases.put(mappedAliasName, new EntityAlias(mappedAliasName, entityAlias.entityClass));
+ });
+ importedRbacView.getRoleDefs().forEach(roleDef -> {
+ new RbacRoleDefinition(findEntityAlias(mapper.map(roleDef.entityAlias.aliasName)), roleDef.role);
+ });
+ importedRbacView.getGrantDefs().forEach(grantDef -> {
+ if (grantDef.grantType() == RbacGrantDefinition.GrantType.ROLE_TO_ROLE) {
+ final var importedGrantDef = findOrCreateGrantDef(
+ findRbacRole(
+ mapper.map(grantDef.getSubRoleDef().entityAlias.aliasName),
+ grantDef.getSubRoleDef().getRole()),
+ findRbacRole(
+ mapper.map(grantDef.getSuperRoleDef().entityAlias.aliasName),
+ grantDef.getSuperRoleDef().getRole())
+ );
+ if (!grantDef.isAssumed()) {
+ importedGrantDef.unassumed();
+ }
+ }
+ });
+ return this;
+ }
+
+ private void verifyVersionColumnExists() {
+ if (stream(rootEntityAlias.entityClass.getDeclaredFields())
+ .noneMatch(f -> f.getAnnotation(Version.class) != null)) {
+ // TODO: convert this into throw Exception once RbacEntity is a base class with @Version field
+ System.err.println("@Version field required in updatable entity " + rootEntityAlias.entityClass);
+ }
+ }
+
+ public RbacGrantBuilder toRole(final String entityAlias, final Role role) {
+ return new RbacGrantBuilder(entityAlias, role);
+ }
+
+ public RbacExampleRole forExampleRole(final String entityAlias, final Role role) {
+ return new RbacExampleRole(entityAlias, role);
+ }
+
+ private RbacGrantDefinition grantRoleToUser(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
+ return findOrCreateGrantDef(roleDefinition, user).toCreate();
+ }
+
+ private RbacGrantDefinition grantPermissionToRole(
+ final RbacPermissionDefinition permDef,
+ final RbacRoleDefinition roleDef) {
+ return findOrCreateGrantDef(permDef, roleDef).toCreate();
+ }
+
+ private RbacGrantDefinition grantSubRoleToSuperRole(
+ final RbacRoleDefinition subRoleDefinition,
+ final RbacRoleDefinition superRoleDefinition) {
+ return findOrCreateGrantDef(subRoleDefinition, superRoleDefinition).toCreate();
+ }
+
+ boolean isRootEntityAlias(final EntityAlias entityAlias) {
+ return entityAlias == this.rootEntityAlias;
+ }
+
+ public boolean isEntityAliasProxy(final EntityAlias entityAlias) {
+ return entityAlias == rootEntityAliasProxy;
+ }
+
+ public SQL getOrderBySqlExpression() {
+ if (orderBySqlExpression == null) {
+ return identityViewSqlQuery;
+ }
+ return orderBySqlExpression;
+ }
+
+ public void generateWithBaseFileName(final String baseFileName) {
+ new RbacViewMermaidFlowchartGenerator(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md"));
+ new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql"));
+ }
+
+ public class RbacGrantBuilder {
+
+ private final RbacRoleDefinition superRoleDef;
+
+ private RbacGrantBuilder(final String entityAlias, final Role role) {
+ this.superRoleDef = findRbacRole(entityAlias, role);
+ }
+
+ public RbacView grantRole(final String entityAlias, final Role role) {
+ findOrCreateGrantDef(findRbacRole(entityAlias, role), superRoleDef).toCreate();
+ return RbacView.this;
+ }
+
+ public RbacView grantPermission(final String entityAliasName, final Permission perm) {
+ final var entityAlias = findEntityAlias(entityAliasName);
+ final var forTable = entityAlias.getRawTableName();
+ findOrCreateGrantDef(findRbacPerm(entityAlias, perm, forTable), superRoleDef).toCreate();
+ return RbacView.this;
+ }
+
+ }
+
+ @Getter
+ @EqualsAndHashCode
+ public class RbacGrantDefinition {
+
+ private final RbacUserReference userDef;
+ private final RbacRoleDefinition superRoleDef;
+ private final RbacRoleDefinition subRoleDef;
+ private final RbacPermissionDefinition permDef;
+ private boolean assumed = true;
+ private boolean toCreate = false;
+
+ @Override
+ public String toString() {
+ final var arrow = isAssumed() ? " --> " : " -- // --> ";
+ return switch (grantType()) {
+ case ROLE_TO_USER -> userDef.toString() + arrow + subRoleDef.toString();
+ case ROLE_TO_ROLE -> superRoleDef + arrow + subRoleDef;
+ case PERM_TO_ROLE -> superRoleDef + arrow + permDef;
+ };
+ }
+
+ RbacGrantDefinition(final RbacRoleDefinition subRoleDef, final RbacRoleDefinition superRoleDef) {
+ this.userDef = null;
+ this.subRoleDef = subRoleDef;
+ this.superRoleDef = superRoleDef;
+ this.permDef = null;
+ register(this);
+ }
+
+ public RbacGrantDefinition(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef) {
+ this.userDef = null;
+ this.subRoleDef = null;
+ this.superRoleDef = roleDef;
+ this.permDef = permDef;
+ register(this);
+ }
+
+ public RbacGrantDefinition(final RbacRoleDefinition roleDef, final RbacUserReference userDef) {
+ this.userDef = userDef;
+ this.subRoleDef = roleDef;
+ this.superRoleDef = null;
+ this.permDef = null;
+ register(this);
+ }
+
+ private void register(final RbacGrantDefinition rbacGrantDefinition) {
+ grantDefs.add(rbacGrantDefinition);
+ }
+
+ @NotNull
+ GrantType grantType() {
+ return permDef != null ? GrantType.PERM_TO_ROLE
+ : userDef != null ? GrantType.ROLE_TO_USER
+ : GrantType.ROLE_TO_ROLE;
+ }
+
+ boolean isAssumed() {
+ return assumed;
+ }
+
+ boolean isToCreate() {
+ return toCreate;
+ }
+
+ RbacGrantDefinition toCreate() {
+ toCreate = true;
+ return this;
+ }
+
+ boolean dependsOnColumn(final String columnName) {
+ return dependsRoleDefOnColumnName(this.superRoleDef, columnName)
+ || dependsRoleDefOnColumnName(this.subRoleDef, columnName);
+ }
+
+ private Boolean dependsRoleDefOnColumnName(final RbacRoleDefinition superRoleDef, final String columnName) {
+ return ofNullable(superRoleDef)
+ .map(r -> r.getEntityAlias().dependsOnColum())
+ .map(d -> columnName.equals(d.column))
+ .orElse(false);
+ }
+
+ public void unassumed() {
+ this.assumed = false;
+ }
+
+ public enum GrantType {
+ ROLE_TO_USER,
+ ROLE_TO_ROLE,
+ PERM_TO_ROLE
+ }
+ }
+
+ public class RbacExampleRole {
+
+ final EntityAlias subRoleEntity;
+ final Role subRole;
+ private EntityAlias superRoleEntity;
+ Role superRole;
+
+ public RbacExampleRole(final String entityAlias, final Role role) {
+ this.subRoleEntity = findEntityAlias(entityAlias);
+ this.subRole = role;
+ }
+
+ public RbacView wouldBeGrantedTo(final String entityAlias, final Role role) {
+ this.superRoleEntity = findEntityAlias(entityAlias);
+ this.superRole = role;
+ return RbacView.this;
+ }
+ }
+
+ @Getter
+ @EqualsAndHashCode
+ public class RbacPermissionDefinition {
+
+ final EntityAlias entityAlias;
+ final Permission permission;
+ final String tableName;
+ final boolean toCreate;
+
+ private RbacPermissionDefinition(final EntityAlias entityAlias, final Permission permission, final String tableName, final boolean toCreate) {
+ this.entityAlias = entityAlias;
+ this.permission = permission;
+ this.tableName = tableName;
+ this.toCreate = toCreate;
+ permDefs.add(this);
+ }
+
+ public RbacView grantedTo(final String entityAlias, final Role role) {
+ findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
+ return RbacView.this;
+ }
+
+ @Override
+ public String toString() {
+ return "perm:" + entityAlias.aliasName + permission + ofNullable(tableName).map(tn -> ":" + tn).orElse("");
+ }
+ }
+
+ @Getter
+ @EqualsAndHashCode
+ public class RbacRoleDefinition {
+
+ private final EntityAlias entityAlias;
+ private final Role role;
+ private boolean toCreate;
+
+ public RbacRoleDefinition(final EntityAlias entityAlias, final Role role) {
+ this.entityAlias = entityAlias;
+ this.role = role;
+ roleDefs.add(this);
+ }
+
+ public RbacRoleDefinition toCreate() {
+ this.toCreate = true;
+ return this;
+ }
+
+ public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
+ return grantRoleToUser(this, findUserRef(userRole));
+ }
+
+ public RbacGrantDefinition permission(final Permission permission) {
+ return grantPermissionToRole(createPermission(entityAlias, permission), this);
+ }
+
+ public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) {
+ final var incomingSuperRole = findRbacRole(entityAlias, role);
+ return grantSubRoleToSuperRole(this, incomingSuperRole);
+ }
+
+ public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) {
+ final var outgoingSubRole = findRbacRole(entityAlias, role);
+ return grantSubRoleToSuperRole(outgoingSubRole, this);
+ }
+
+ @Override
+ public String toString() {
+ return "role:" + entityAlias.aliasName + role;
+ }
+ }
+
+ public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) {
+ return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow();
+ }
+
+ @EqualsAndHashCode
+ public class RbacUserReference {
+
+ public enum UserRole {
+ GLOBAL_ADMIN,
+ CREATOR
+ }
+
+ final UserRole role;
+
+ public RbacUserReference(final UserRole creator) {
+ this.role = creator;
+ userDefs.add(this);
+ }
+
+ @Override
+ public String toString() {
+ return "user:" + role;
+ }
+ }
+
+ EntityAlias findEntityAlias(final String aliasName) {
+ final var found = entityAliases.get(aliasName);
+ if (found == null) {
+ throw new IllegalArgumentException("entityAlias not found: " + aliasName);
+ }
+ return found;
+ }
+
+ RbacRoleDefinition findRbacRole(final EntityAlias entityAlias, final Role role) {
+ return roleDefs.stream()
+ .filter(r -> r.getEntityAlias() == entityAlias && r.getRole().equals(role))
+ .findFirst()
+ .orElseGet(() -> new RbacRoleDefinition(entityAlias, role));
+ }
+
+ public RbacRoleDefinition findRbacRole(final String entityAliasName, final Role role) {
+ return findRbacRole(findEntityAlias(entityAliasName), role);
+
+ }
+
+ RbacPermissionDefinition findRbacPerm(final EntityAlias entityAlias, final Permission perm, String tableName) {
+ return permDefs.stream()
+ .filter(p -> p.getEntityAlias() == entityAlias && p.getPermission() == perm)
+ .findFirst()
+ .orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate
+ }
+
+
+ RbacPermissionDefinition findRbacPerm(final EntityAlias entityAlias, final Permission perm) {
+ return findRbacPerm(entityAlias, perm, null);
+ }
+
+ public RbacPermissionDefinition findRbacPerm(final String entityAliasName, final Permission perm, String tableName) {
+ return findRbacPerm(findEntityAlias(entityAliasName), perm, tableName);
+ }
+
+ public RbacPermissionDefinition findRbacPerm(final String entityAliasName, final Permission perm) {
+ return findRbacPerm(findEntityAlias(entityAliasName), perm);
+ }
+
+ private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
+ return grantDefs.stream()
+ .filter(g -> g.subRoleDef == roleDefinition && g.userDef == user)
+ .findFirst()
+ .orElseGet(() -> new RbacGrantDefinition(roleDefinition, user));
+ }
+
+ private RbacGrantDefinition findOrCreateGrantDef(final RbacPermissionDefinition permDef, final RbacRoleDefinition roleDef) {
+ return grantDefs.stream()
+ .filter(g -> g.permDef == permDef && g.subRoleDef == roleDef)
+ .findFirst()
+ .orElseGet(() -> new RbacGrantDefinition(permDef, roleDef));
+ }
+
+ private RbacGrantDefinition findOrCreateGrantDef(
+ final RbacRoleDefinition subRoleDefinition,
+ final RbacRoleDefinition superRoleDefinition) {
+ return grantDefs.stream()
+ .filter(g -> g.subRoleDef == subRoleDefinition && g.superRoleDef == superRoleDefinition)
+ .findFirst()
+ .orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
+ }
+
+ record EntityAlias(String aliasName, Class extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) {
+
+ public EntityAlias(final String aliasName) {
+ this(aliasName, null, null, null, false);
+ }
+
+ public EntityAlias(final String aliasName, final Class extends RbacObject> entityClass) {
+ this(aliasName, entityClass, null, null, false);
+ }
+
+ boolean isGlobal() {
+ return aliasName().equals("global");
+ }
+
+ boolean isPlaceholder() {
+ return entityClass == null;
+ }
+
+ @NotNull
+ @Override
+ public SQL fetchSql() {
+ if (fetchSql == null) {
+ return SQL.noop();
+ }
+ return switch (fetchSql.part) {
+ case SQL_QUERY -> fetchSql;
+ case AUTO_FETCH ->
+ SQL.query("SELECT * FROM " + getRawTableName() + " WHERE uuid = ${ref}." + dependsOnColum.column);
+ default -> throw new IllegalStateException("unexpected SQL definition: " + fetchSql);
+ };
+ }
+
+ public boolean hasFetchSql() {
+ return fetchSql != null;
+ }
+
+ private String withoutEntitySuffix(final String simpleEntityName) {
+ return simpleEntityName.substring(0, simpleEntityName.length() - "Entity".length());
+ }
+
+ String simpleName() {
+ return isGlobal()
+ ? aliasName
+ : uncapitalize(withoutEntitySuffix(entityClass.getSimpleName()));
+ }
+
+ String getRawTableName() {
+ if ( aliasName.equals("global")) {
+ return "global"; // TODO: maybe we should introduce a GlobalEntity class?
+ }
+ return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
+ }
+
+ String dependsOnColumName() {
+ if (dependsOnColum == null) {
+ throw new IllegalStateException(
+ "Entity " + aliasName + "(" + entityClass.getSimpleName() + ")" + ": please add dependsOnColum");
+ }
+ return dependsOnColum.column;
+ }
+ }
+
+ public static String withoutRvSuffix(final String tableName) {
+ return tableName.substring(0, tableName.length() - "_rv".length());
+ }
+
+ public record Role(String roleName) {
+
+ public static final Role OWNER = new Role("owner");
+ public static final Role ADMIN = new Role("admin");
+ public static final Role AGENT = new Role("agent");
+ public static final Role TENANT = new Role("tenant");
+ public static final Role REFERRER = new Role("referrer");
+
+ @Override
+ public String toString() {
+ return ":" + roleName;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
+ }
+ }
+
+ public record Permission(String permission) {
+
+ public static final Permission INSERT = new Permission("INSERT");
+ public static final Permission DELETE = new Permission("DELETE");
+ public static final Permission UPDATE = new Permission("UPDATE");
+ public static final Permission SELECT = new Permission("SELECT");
+
+ public static Permission custom(final String permission) {
+ return new Permission(permission);
+ }
+
+ @Override
+ public String toString() {
+ return ":" + permission;
+ }
+ }
+
+ public static class SQL {
+
+ /**
+ * DSL method to specify an SQL SELECT expression which fetches the related entity,
+ * using the reference `${ref}` of the root entity.
+ * `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
+ * `into ...` will be added with a variable name prefixed with either `new` or `old`.
+ *
+ * @param sql an SQL SELECT expression (not ending with ';)
+ * @return the wrapped SQL expression
+ */
+ public static SQL fetchedBySql(final String sql) {
+ validateExpression(sql);
+ return new SQL(sql, Part.SQL_QUERY);
+ }
+
+ /**
+ * DSL method to specify that a related entity is to be fetched by a simple SELECT statement
+ * using the raw table from the @Table statement of the entity to fetch
+ * and the dependent column of the root entity.
+ *
+ * @return the wrapped SQL definition object
+ */
+ public static SQL autoFetched() {
+ return new SQL(null, Part.AUTO_FETCH);
+ }
+
+ /**
+ * DSL method to explicitly specify that there is no SQL query.
+ *
+ * @return a wrapped SQL definition object representing a noop query
+ */
+ public static SQL noop() {
+ return new SQL(null, Part.NOOP);
+ }
+
+ /**
+ * Generic DSL method to specify an SQL SELECT expression.
+ *
+ * @param sql an SQL SELECT expression (not ending with ';)
+ * @return the wrapped SQL expression
+ */
+ public static SQL query(final String sql) {
+ validateExpression(sql);
+ return new SQL(sql, Part.SQL_QUERY);
+ }
+
+ /**
+ * Generic DSL method to specify an SQL SELECT expression by just the projection part.
+ *
+ * @param projection an SQL SELECT expression, the list of columns after 'SELECT'
+ * @return the wrapped SQL projection
+ */
+ public static SQL projection(final String projection) {
+ validateProjection(projection);
+ return new SQL(projection, Part.SQL_PROJECTION);
+ }
+
+ public static SQL expression(final String sqlExpression) {
+ // TODO: validate
+ return new SQL(sqlExpression, Part.SQL_EXPRESSION);
+ }
+
+ enum Part {
+ NOOP,
+ SQL_QUERY,
+ AUTO_FETCH,
+ SQL_PROJECTION,
+ SQL_EXPRESSION
+ }
+
+ final String sql;
+ final Part part;
+
+ private SQL(final String sql, final Part part) {
+ this.sql = sql;
+ this.part = part;
+ }
+
+ private static void validateProjection(final String projection) {
+ if (projection.toUpperCase().matches("[ \t]*$SELECT[ \t]")) {
+ throw new IllegalArgumentException("SQL projection must not start with 'SELECT': " + projection);
+ }
+ if (projection.matches(";[ \t]*$")) {
+ throw new IllegalArgumentException("SQL projection must not end with ';': " + projection);
+ }
+ }
+
+ private static void validateExpression(final String sql) {
+ if (sql.matches(";[ \t]*$")) {
+ throw new IllegalArgumentException("SQL expression must not end with ';': " + sql);
+ }
+ }
+ }
+
+ public static class Column {
+
+ public static Column dependsOnColumn(final String column) {
+ return new Column(column);
+ }
+
+ public final String column;
+
+ private Column(final String column) {
+ this.column = column;
+ }
+ }
+
+ private static class AliasNameMapper {
+
+ private final RbacView importedRbacView;
+ private final String outerAliasName;
+
+ private final Set outerAliasNames;
+
+ AliasNameMapper(final RbacView importedRbacView, final String outerAliasName, final Set outerAliasNames) {
+ this.importedRbacView = importedRbacView;
+ this.outerAliasName = outerAliasName;
+ this.outerAliasNames = (outerAliasNames == null) ? Collections.emptySet() : outerAliasNames;
+ }
+
+ String map(final String originalAliasName) {
+ if (outerAliasNames.contains(originalAliasName) || originalAliasName.equals("global")) {
+ return originalAliasName;
+ }
+ if (originalAliasName.equals(importedRbacView.rootEntityAlias.aliasName)) {
+ return outerAliasName;
+ }
+ return outerAliasName + "." + originalAliasName;
+ }
+ }
+
+ public static void main(String[] args) {
+ Stream.of(
+ TestCustomerEntity.class,
+ TestPackageEntity.class,
+ TestDomainEntity.class,
+ HsOfficePersonEntity.class,
+ HsOfficePartnerEntity.class,
+ HsOfficePartnerDetailsEntity.class,
+ HsOfficeBankAccountEntity.class,
+ HsOfficeDebitorEntity.class,
+ HsOfficeRelationshipEntity.class,
+ HsOfficeCoopAssetsTransactionEntity.class,
+ HsOfficeContactEntity.class,
+ HsOfficeSepaMandateEntity.class,
+ HsOfficeCoopSharesTransactionEntity.class,
+ HsOfficeMembershipEntity.class
+ ).forEach(c -> {
+ final Method mainMethod = stream(c.getMethods()).filter(
+ m -> isStatic(m.getModifiers()) && m.getName().equals("main")
+ )
+ .findFirst()
+ .orElse(null);
+ if (mainMethod != null) {
+ try {
+ mainMethod.invoke(null, new Object[] { null });
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ System.err.println("no main method in: " + c.getName());
+ }
+ });
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
new file mode 100644
index 00000000..ccef566d
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
@@ -0,0 +1,164 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.file.*;
+import java.time.LocalDateTime;
+
+import static java.util.stream.Collectors.joining;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
+
+public class RbacViewMermaidFlowchartGenerator {
+
+ public static final String HOSTSHARING_DARK_ORANGE = "#dd4901";
+ public static final String HOSTSHARING_LIGHT_ORANGE = "#feb28c";
+ public static final String HOSTSHARING_DARK_BLUE = "#274d6e";
+ public static final String HOSTSHARING_LIGHT_BLUE = "#99bcdb";
+ private final RbacView rbacDef;
+ private final StringWriter flowchart = new StringWriter();
+
+ public RbacViewMermaidFlowchartGenerator(final RbacView rbacDef) {
+ this.rbacDef = rbacDef;
+ flowchart.writeLn("""
+ %%{init:{'flowchart':{'htmlLabels':false}}}%%
+ flowchart TB
+ """);
+ renderEntitySubgraphs();
+ renderGrants();
+ }
+ private void renderEntitySubgraphs() {
+ rbacDef.getEntityAliases().values().stream()
+ .filter(entityAlias -> !rbacDef.isEntityAliasProxy(entityAlias))
+ .filter(entityAlias -> !entityAlias.isPlaceholder())
+ .forEach(this::renderEntitySubgraph);
+ }
+
+ private void renderEntitySubgraph(final RbacView.EntityAlias entity) {
+ final var color = rbacDef.isRootEntityAlias(entity) ? HOSTSHARING_DARK_ORANGE
+ : entity.isSubEntity() ? HOSTSHARING_LIGHT_ORANGE
+ : HOSTSHARING_LIGHT_BLUE;
+ flowchart.writeLn("""
+ subgraph %{aliasName}["`**%{aliasName}**`"]
+ direction TB
+ style %{aliasName} fill:%{fillColor},stroke:%{strokeColor},stroke-width:8px
+ """
+ .replace("%{aliasName}", entity.aliasName())
+ .replace("%{fillColor}", color )
+ .replace("%{strokeColor}", HOSTSHARING_DARK_BLUE ));
+
+ flowchart.indented( () -> {
+ rbacDef.getEntityAliases().values().stream()
+ .filter(e -> e.aliasName().startsWith(entity.aliasName() + "."))
+ .forEach(this::renderEntitySubgraph);
+
+ wrapOutputInSubgraph(entity.aliasName() + ":roles", color,
+ rbacDef.getRoleDefs().stream()
+ .filter(r -> r.getEntityAlias() == entity)
+ .map(this::roleDef)
+ .collect(joining("\n")));
+
+ wrapOutputInSubgraph(entity.aliasName() + ":permissions", color,
+ rbacDef.getPermDefs().stream()
+ .filter(p -> p.getEntityAlias() == entity)
+ .map(this::permDef)
+ .collect(joining("\n")));
+
+ if (rbacDef.isRootEntityAlias(entity) && rbacDef.getRootEntityAliasProxy() != null ) {
+ renderEntitySubgraph(rbacDef.getRootEntityAliasProxy());
+ }
+
+ });
+ flowchart.chopEmptyLines();
+ flowchart.writeLn("end");
+ flowchart.writeLn();
+ }
+
+ private void wrapOutputInSubgraph(final String name, final String color, final String content) {
+ if (!StringUtils.isEmpty(content)) {
+ flowchart.ensureSingleEmptyLine();
+ flowchart.writeLn("subgraph " + name + "[ ]\n");
+ flowchart.indented(() -> {
+ flowchart.writeLn("style %{aliasName} fill:%{fillColor},stroke:white"
+ .replace("%{aliasName}", name)
+ .replace("%{fillColor}", color));
+ flowchart.writeLn();
+ flowchart.writeLn(content);
+ });
+ flowchart.chopEmptyLines();
+ flowchart.writeLn("end");
+ flowchart.writeLn();
+ }
+ }
+
+ private void renderGrants() {
+ renderGrants(ROLE_TO_USER, "%% granting roles to users");
+ renderGrants(ROLE_TO_ROLE, "%% granting roles to roles");
+ renderGrants(PERM_TO_ROLE, "%% granting permissions to roles");
+ }
+
+ private void renderGrants(final RbacView.RbacGrantDefinition.GrantType grantType, final String comment) {
+ final var grantsOfRequestedType = rbacDef.getGrantDefs().stream()
+ .filter(g -> g.grantType() == grantType)
+ .toList();
+ if ( !grantsOfRequestedType.isEmpty()) {
+ flowchart.ensureSingleEmptyLine();
+ flowchart.writeLn(comment);
+ grantsOfRequestedType.forEach(g -> flowchart.writeLn(grantDef(g)));
+ }
+ }
+
+ private String grantDef(final RbacView.RbacGrantDefinition grant) {
+ final var arrow = (grant.isToCreate() ? " ==>" : " -.->")
+ + (grant.isAssumed() ? " " : "|XX| ");
+ return switch (grant.grantType()) {
+ case ROLE_TO_USER ->
+ // TODO: other user types not implemented yet
+ "user:creator" + arrow + roleId(grant.getSubRoleDef());
+ case ROLE_TO_ROLE ->
+ roleId(grant.getSuperRoleDef()) + arrow + roleId(grant.getSubRoleDef());
+ case PERM_TO_ROLE -> roleId(grant.getSuperRoleDef()) + arrow + permId(grant.getPermDef());
+ };
+ }
+
+ private String permDef(final RbacView.RbacPermissionDefinition perm) {
+ return permId(perm) + "{{" + perm.getEntityAlias().aliasName() + perm.getPermission() + "}}";
+ }
+
+ private static String permId(final RbacView.RbacPermissionDefinition permDef) {
+ return "perm:" + permDef.getEntityAlias().aliasName() + permDef.getPermission();
+ }
+
+ private String roleDef(final RbacView.RbacRoleDefinition roleDef) {
+ return roleId(roleDef) + "[[" + roleDef.getEntityAlias().aliasName() + roleDef.getRole() + "]]";
+ }
+
+ private static String roleId(final RbacView.RbacRoleDefinition r) {
+ return "role:" + r.getEntityAlias().aliasName() + r.getRole();
+ }
+
+ @Override
+ public String toString() {
+ return flowchart.toString();
+ }
+
+ @SneakyThrows
+ public void generateToMarkdownFile(final Path path) {
+ Files.writeString(
+ path,
+ """
+ ### rbac %{entityAlias}
+
+ This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}.
+
+ ```mermaid
+ %{flowchart}
+ ```
+ """
+ .replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
+ .replace("%{timestamp}", LocalDateTime.now().toString())
+ .replace("%{flowchart}", flowchart.toString()),
+ StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+ System.out.println("Markdown-File: " + path.toAbsolutePath());
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
new file mode 100644
index 00000000..eb8f3534
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
@@ -0,0 +1,52 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import lombok.SneakyThrows;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.time.LocalDateTime;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+
+public class RbacViewPostgresGenerator {
+
+ private final RbacView rbacDef;
+ private final String liqibaseTagPrefix;
+ private final StringWriter plPgSql = new StringWriter();
+
+ public RbacViewPostgresGenerator(final RbacView forRbacDef) {
+ rbacDef = forRbacDef;
+ liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
+ plPgSql.writeLn("""
+ --liquibase formatted sql
+ -- This code generated was by ${generator} at ${timestamp}.
+ """,
+ with("generator", getClass().getSimpleName()),
+ with("timestamp", LocalDateTime.now().toString()),
+ with("ref", NEW.name()));
+
+ new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ new RbacRoleDescriptorsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ new RolesGrantsAndPermissionsGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ new InsertTriggerGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ new RbacIdentityViewGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ new RbacRestrictedViewGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
+ }
+
+ @Override
+ public String toString() {
+ return plPgSql.toString();
+}
+
+ @SneakyThrows
+ public void generateToChangeLog(final Path outputPath) {
+ Files.writeString(
+ outputPath,
+ toString(),
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ System.out.println(outputPath.toAbsolutePath());
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
new file mode 100644
index 00000000..edb1f609
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
@@ -0,0 +1,507 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toSet;
+import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
+import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.OLD;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
+import static org.apache.commons.lang3.StringUtils.capitalize;
+import static org.apache.commons.lang3.StringUtils.uncapitalize;
+
+class RolesGrantsAndPermissionsGenerator {
+
+ private final RbacView rbacDef;
+ private final Set rbacGrants = new HashSet<>();
+ private final String liquibaseTagPrefix;
+ private final String simpleEntityName;
+ private final String simpleEntityVarName;
+ private final String rawTableName;
+
+ RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
+ this.rbacDef = rbacDef;
+ this.rbacGrants.addAll(rbacDef.getGrantDefs().stream()
+ .filter(RbacView.RbacGrantDefinition::isToCreate)
+ .collect(toSet()));
+ this.liquibaseTagPrefix = liquibaseTagPrefix;
+
+ simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
+ simpleEntityName = capitalize(simpleEntityVarName);
+ rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
+ }
+
+ void generateTo(final StringWriter plPgSql) {
+ generateInsertTrigger(plPgSql);
+ if (hasAnyUpdatableEntityAliases()) {
+ generateUpdateTrigger(plPgSql);
+ }
+ }
+
+ private void generateHeader(final StringWriter plPgSql, final String triggerType) {
+ plPgSql.writeLn("""
+ -- ============================================================================
+ --changeset ${liquibaseTagPrefix}-rbac-${triggerType}-trigger:1 endDelimiter:--//
+ -- ----------------------------------------------------------------------------
+ """,
+ with("liquibaseTagPrefix", liquibaseTagPrefix),
+ with("triggerType", triggerType));
+ }
+
+ private void generateInsertTriggerFunction(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ /*
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
+ */
+
+ create or replace procedure buildRbacSystemFor${simpleEntityName}(
+ NEW ${rawTableName}
+ )
+ language plpgsql as $$
+
+ declare
+ """
+ .replace("${simpleEntityName}", simpleEntityName)
+ .replace("${rawTableName}", rawTableName));
+
+ plPgSql.chopEmptyLines();
+ plPgSql.indented(() -> {
+ referencedEntityAliases()
+ .forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
+ });
+
+ plPgSql.writeLn();
+ plPgSql.writeLn("begin");
+ plPgSql.indented(() -> {
+ plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
+ generateCreateRolesAndGrantsAfterInsert(plPgSql);
+ plPgSql.ensureSingleEmptyLine();
+ plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
+ });
+ plPgSql.writeLn("end; $$;");
+ plPgSql.writeLn();
+ }
+
+ private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ /*
+ Called from the AFTER UPDATE TRIGGER to re-wire the grants.
+ */
+
+ create or replace procedure updateRbacRulesFor${simpleEntityName}(
+ OLD ${rawTableName},
+ NEW ${rawTableName}
+ )
+ language plpgsql as $$
+
+ declare
+ """
+ .replace("${simpleEntityName}", simpleEntityName)
+ .replace("${rawTableName}", rawTableName));
+
+ plPgSql.chopEmptyLines();
+ plPgSql.indented(() -> {
+ updatableEntityAliases()
+ .forEach((ea) -> {
+ plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
+ plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
+ });
+ });
+
+ plPgSql.writeLn();
+ plPgSql.writeLn("begin");
+ plPgSql.indented(() -> {
+ plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
+ generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
+ plPgSql.ensureSingleEmptyLine();
+ plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
+ });
+ plPgSql.writeLn("end; $$;");
+ plPgSql.writeLn();
+ }
+
+ private boolean hasAnyUpdatableEntityAliases() {
+ return updatableEntityAliases().anyMatch(e -> true);
+ }
+
+ private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
+ referencedEntityAliases()
+ .forEach((ea) -> plPgSql.writeLn(
+ ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
+ with("ref", NEW.name())));
+
+ createRolesWithGrantsSql(plPgSql, OWNER);
+ createRolesWithGrantsSql(plPgSql, ADMIN);
+ createRolesWithGrantsSql(plPgSql, AGENT);
+ createRolesWithGrantsSql(plPgSql, TENANT);
+ createRolesWithGrantsSql(plPgSql, REFERRER);
+
+ generateGrants(plPgSql, ROLE_TO_USER);
+ generateGrants(plPgSql, ROLE_TO_ROLE);
+ generateGrants(plPgSql, PERM_TO_ROLE);
+ }
+
+ private Stream referencedEntityAliases() {
+ return rbacDef.getEntityAliases().values().stream()
+ .filter(ea -> !rbacDef.isRootEntityAlias(ea))
+ .filter(ea -> ea.dependsOnColum() != null)
+ .filter(ea -> ea.entityClass() != null)
+ .filter(ea -> ea.fetchSql() != null);
+ }
+
+ private Stream updatableEntityAliases() {
+ return referencedEntityAliases()
+ .filter(ea -> rbacDef.getUpdatableColumns().contains(ea.dependsOnColum().column));
+ }
+
+ private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
+ plPgSql.ensureSingleEmptyLine();
+
+ updatableEntityAliases()
+ .forEach((ea) -> {
+ plPgSql.writeLn(
+ ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
+ with("ref", OLD.name()));
+ plPgSql.writeLn(
+ ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
+ with("ref", NEW.name()));
+ });
+
+ updatableEntityAliases()
+ .map(RbacView.EntityAlias::dependsOnColum)
+ .map(c -> c.column)
+ .sorted()
+ .distinct()
+ .forEach(columnName -> {
+ plPgSql.writeLn();
+ plPgSql.writeLn("if NEW." + columnName + " <> OLD." + columnName + " then");
+ plPgSql.indented(() -> {
+ updateGrantsDependingOn(plPgSql, columnName);
+ });
+ plPgSql.writeLn("end if;");
+ });
+ }
+
+ private boolean isUpdatable(final RbacView.Column c) {
+ return rbacDef.getUpdatableColumns().contains(c);
+ }
+
+ private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
+ rbacDef.getGrantDefs().stream()
+ .filter(RbacView.RbacGrantDefinition::isToCreate)
+ .filter(g -> g.dependsOnColumn(columnName))
+ .forEach(g -> {
+ plPgSql.ensureSingleEmptyLine();
+ plPgSql.writeLn(generateRevoke(g));
+ plPgSql.writeLn(generateGrant(g));
+ plPgSql.writeLn();
+ });
+ }
+
+ private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
+ plPgSql.ensureSingleEmptyLine();
+ rbacGrants.stream()
+ .filter(g -> g.grantType() == grantType)
+ .map(this::generateGrant)
+ .sorted()
+ .forEach(text -> plPgSql.writeLn(text));
+ }
+
+ private String generateRevoke(RbacView.RbacGrantDefinition grantDef) {
+ return switch (grantDef.grantType()) {
+ case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
+ case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
+ .replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
+ .replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
+ case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
+ .replace("${permRef}", findPerm(OLD, grantDef.getPermDef()))
+ .replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
+ };
+ }
+
+ private String generateGrant(RbacView.RbacGrantDefinition grantDef) {
+ return switch (grantDef.grantType()) {
+ case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
+ case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
+ .replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
+ .replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef()))
+ .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
+ case PERM_TO_ROLE ->
+ grantDef.getPermDef().getPermission() == INSERT ? ""
+ : "call grantPermissionToRole(${permRef}, ${superRoleRef});"
+ .replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
+ .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
+ };
+ }
+
+ private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
+ return permRef("findPermissionId", ref, permDef);
+ }
+
+ private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
+ return permRef("createPermission", ref, permDef);
+ }
+
+ private String permRef(final String functionName, final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
+ return "${prefix}(${entityRef}.uuid, '${perm}')"
+ .replace("${prefix}", functionName)
+ .replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
+ ? ref.name()
+ : refVarName(ref, permDef.entityAlias))
+ .replace("${perm}", permDef.permission.permission());
+ }
+
+ private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
+ return ref.name().toLowerCase() + capitalize(entityAlias.aliasName());
+ }
+
+ private String roleRef(final PostgresTriggerReference rootRefVar, final RbacView.RbacRoleDefinition roleDef) {
+ if (roleDef == null) {
+ System.out.println("null");
+ }
+ if (roleDef.getEntityAlias().isGlobal()) {
+ return "globalAdmin()";
+ }
+ final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias());
+ return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().roleName())
+ + "(" + entityRefVar + ")";
+ }
+
+ private String entityRefVar(
+ final PostgresTriggerReference rootRefVar,
+ final RbacView.EntityAlias entityAlias) {
+ return rbacDef.isRootEntityAlias(entityAlias)
+ ? rootRefVar.name()
+ : rootRefVar.name().toLowerCase() + capitalize(entityAlias.aliasName());
+ }
+
+ private void createRolesWithGrantsSql(final StringWriter plPgSql, final RbacView.Role role) {
+
+ final var isToCreate = rbacDef.getRoleDefs().stream()
+ .filter(roleDef -> rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) && roleDef.getRole() == role)
+ .findFirst().map(RbacView.RbacRoleDefinition::isToCreate).orElse(false);
+ if (!isToCreate) {
+ return;
+ }
+
+ plPgSql.writeLn();
+ plPgSql.writeLn("perform createRoleWithGrants(");
+ plPgSql.indented(() -> {
+ plPgSql.writeLn("${simpleVarName)${roleSuffix}(NEW),"
+ .replace("${simpleVarName)", simpleEntityVarName)
+ .replace("${roleSuffix}", capitalize(role.roleName())));
+
+ generatePermissionsForRole(plPgSql, role);
+
+ generateUserGrantsForRole(plPgSql, role);
+
+ generateIncomingSuperRolesForRole(plPgSql, role);
+
+ generateOutgoingSubRolesForRole(plPgSql, role);
+
+ plPgSql.chopTail(",\n");
+ plPgSql.writeLn();
+ });
+
+ plPgSql.writeLn(");");
+ }
+
+ private void generateUserGrantsForRole(final StringWriter plPgSql, final RbacView.Role role) {
+ final var grantsToUsers = findGrantsToUserForRole(rbacDef.getRootEntityAlias(), role);
+ if (!grantsToUsers.isEmpty()) {
+ final var arrayElements = grantsToUsers.stream()
+ .map(RbacView.RbacGrantDefinition::getUserDef)
+ .map(this::toPlPgSqlReference)
+ .toList();
+ plPgSql.indented(() ->
+ plPgSql.writeLn("userUuids => array[" + joinArrayElements(arrayElements, 2) + "],\n"));
+ rbacGrants.removeAll(grantsToUsers);
+ }
+ }
+
+ private void generatePermissionsForRole(final StringWriter plPgSql, final RbacView.Role role) {
+ final var permissionGrantsForRole = findPermissionsGrantsForRole(rbacDef.getRootEntityAlias(), role);
+ if (!permissionGrantsForRole.isEmpty()) {
+ final var arrayElements = permissionGrantsForRole.stream()
+ .map(RbacView.RbacGrantDefinition::getPermDef)
+ .map(RbacPermissionDefinition::getPermission)
+ .map(RbacView.Permission::permission)
+ .map(p -> "'" + p + "'")
+ .sorted()
+ .toList();
+ plPgSql.indented(() ->
+ plPgSql.writeLn("permissions => array[" + joinArrayElements(arrayElements, 3) + "],\n"));
+ rbacGrants.removeAll(permissionGrantsForRole);
+ }
+ }
+
+ private void generateIncomingSuperRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
+ final var incomingGrants = findIncomingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
+ if (!incomingGrants.isEmpty()) {
+ final var arrayElements = incomingGrants.stream()
+ .map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
+ .toList();
+ plPgSql.indented(() ->
+ plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
+ rbacGrants.removeAll(incomingGrants);
+ }
+ }
+
+ private void generateOutgoingSubRolesForRole(final StringWriter plPgSql, final RbacView.Role role) {
+ final var outgoingGrants = findOutgoingSuperRolesForRole(rbacDef.getRootEntityAlias(), role);
+ if (!outgoingGrants.isEmpty()) {
+ final var arrayElements = outgoingGrants.stream()
+ .map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
+ .toList();
+ plPgSql.indented(() ->
+ plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
+ rbacGrants.removeAll(outgoingGrants);
+ }
+ }
+
+ private String joinArrayElements(final List arrayElements, final int singleLineLimit) {
+ return arrayElements.size() <= singleLineLimit
+ ? String.join(", ", arrayElements)
+ : arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
+ }
+
+ private Set findPermissionsGrantsForRole(
+ final RbacView.EntityAlias entityAlias,
+ final RbacView.Role role) {
+ final var roleDef = rbacDef.findRbacRole(entityAlias, role);
+ return rbacGrants.stream()
+ .filter(g -> g.grantType() == PERM_TO_ROLE && g.getSuperRoleDef() == roleDef)
+ .collect(toSet());
+ }
+
+ private Set findGrantsToUserForRole(
+ final RbacView.EntityAlias entityAlias,
+ final RbacView.Role role) {
+ final var roleDef = rbacDef.findRbacRole(entityAlias, role);
+ return rbacGrants.stream()
+ .filter(g -> g.grantType() == ROLE_TO_USER && g.getSubRoleDef() == roleDef)
+ .collect(toSet());
+ }
+
+ private Set findIncomingSuperRolesForRole(
+ final RbacView.EntityAlias entityAlias,
+ final RbacView.Role role) {
+ final var roleDef = rbacDef.findRbacRole(entityAlias, role);
+ return rbacGrants.stream()
+ .filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSubRoleDef() == roleDef)
+ .collect(toSet());
+ }
+
+ private Set findOutgoingSuperRolesForRole(
+ final RbacView.EntityAlias entityAlias,
+ final RbacView.Role role) {
+ final var roleDef = rbacDef.findRbacRole(entityAlias, role);
+ return rbacGrants.stream()
+ .filter(g -> g.grantType() == ROLE_TO_ROLE && g.getSuperRoleDef() == roleDef)
+ .filter(g -> g.getSubRoleDef().getEntityAlias() != entityAlias)
+ .collect(toSet());
+ }
+
+ private void generateInsertTrigger(final StringWriter plPgSql) {
+
+ generateHeader(plPgSql, "insert");
+ generateInsertTriggerFunction(plPgSql);
+
+ plPgSql.writeLn("""
+ /*
+ AFTER INSERT TRIGGER to create the role+grant structure for a new ${rawTableName} row.
+ */
+
+ create or replace function insertTriggerFor${simpleEntityName}_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+ begin
+ call buildRbacSystemFor${simpleEntityName}(NEW);
+ return NEW;
+ end; $$;
+
+ create trigger insertTriggerFor${simpleEntityName}_tg
+ after insert on ${rawTableName}
+ for each row
+ execute procedure insertTriggerFor${simpleEntityName}_tf();
+ """
+ .replace("${simpleEntityName}", simpleEntityName)
+ .replace("${rawTableName}", rawTableName)
+ );
+
+ generateFooter(plPgSql);
+ }
+
+ private void generateUpdateTrigger(final StringWriter plPgSql) {
+
+ generateHeader(plPgSql, "update");
+ generateUpdateTriggerFunction(plPgSql);
+
+ plPgSql.writeLn("""
+ /*
+ AFTER INSERT TRIGGER to re-wire the grant structure for a new ${rawTableName} row.
+ */
+
+ create or replace function updateTriggerFor${simpleEntityName}_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+ begin
+ call updateRbacRulesFor${simpleEntityName}(OLD, NEW);
+ return NEW;
+ end; $$;
+
+ create trigger updateTriggerFor${simpleEntityName}_tg
+ after update on ${rawTableName}
+ for each row
+ execute procedure updateTriggerFor${simpleEntityName}_tf();
+ """
+ .replace("${simpleEntityName}", simpleEntityName)
+ .replace("${rawTableName}", rawTableName)
+ );
+
+ generateFooter(plPgSql);
+ }
+
+ private static void generateFooter(final StringWriter plPgSql) {
+ plPgSql.writeLn("--//");
+ plPgSql.writeLn();
+ }
+
+ private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
+ return switch (userRef.role) {
+ case CREATOR -> "currentUserUuid()";
+ default -> throw new IllegalArgumentException("unknown user role: " + userRef);
+ };
+ }
+
+ private String toPlPgSqlReference(
+ final PostgresTriggerReference triggerRef,
+ final RbacView.RbacRoleDefinition roleDef,
+ final boolean assumed) {
+ final var assumedArg = assumed ? "" : ", unassumed()";
+ return toRoleRef(roleDef) +
+ (roleDef.getEntityAlias().isGlobal() ? ( assumed ? "()" : "(unassumed())")
+ : rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
+ : "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + assumedArg + ")");
+ }
+
+ private static String toRoleRef(final RbacView.RbacRoleDefinition roleDef) {
+ return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
+ }
+
+ private static String toTriggerReference(
+ final PostgresTriggerReference triggerRef,
+ final RbacView.EntityAlias entityAlias) {
+ return triggerRef.name().toLowerCase() + capitalize(entityAlias.aliasName());
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
new file mode 100644
index 00000000..512ec72d
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
@@ -0,0 +1,111 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.regex.Pattern;
+
+import static java.util.Arrays.stream;
+import static java.util.stream.Collectors.joining;
+
+public class StringWriter {
+
+ private final StringBuilder string = new StringBuilder();
+ private int indentLevel = 0;
+
+ static VarDef with(final String var, final String name) {
+ return new VarDef(var, name);
+ }
+
+ void writeLn(final String text) {
+ string.append( indented(text));
+ writeLn();
+ }
+
+ void writeLn(final String text, final VarDef... varDefs) {
+ string.append( indented( new VarReplacer(varDefs).apply(text) ));
+ writeLn();
+ }
+
+ void writeLn() {
+ string.append( "\n");
+ }
+
+ void indent() {
+ ++indentLevel;
+ }
+
+ void unindent() {
+ --indentLevel;
+ }
+
+ void indented(final Runnable indented) {
+ indent();
+ indented.run();
+ unindent();
+ }
+
+ boolean chopTail(final String tail) {
+ if (string.toString().endsWith(tail)) {
+ string.setLength(string.length() - tail.length());
+ return true;
+ }
+ return false;
+ }
+
+ void chopEmptyLines() {
+ while (string.toString().endsWith("\n\n")) {
+ string.setLength(string.length() - 1);
+ };
+ }
+
+ void ensureSingleEmptyLine() {
+ chopEmptyLines();
+ writeLn();
+ }
+
+ @Override
+ public String toString() {
+ return string.toString();
+ }
+
+ public static String indented(final String text, final int indentLevel) {
+ final var indentation = StringUtils.repeat(" ", indentLevel);
+ final var indented = stream(text.split("\n"))
+ .map(line -> line.trim().isBlank() ? "" : indentation + line)
+ .collect(joining("\n"));
+ return indented;
+ }
+
+ private String indented(final String text) {
+ if ( indentLevel == 0) {
+ return text;
+ }
+ return indented(text, indentLevel);
+ }
+
+ record VarDef(String name, String value){}
+
+ private static final class VarReplacer {
+
+ private final VarDef[] varDefs;
+ private String text;
+
+ private VarReplacer(VarDef[] varDefs) {
+ this.varDefs = varDefs;
+ }
+
+ String apply(final String textToAppend) {
+ try {
+ text = textToAppend;
+ stream(varDefs).forEach(varDef -> {
+ final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
+ final var matcher = pattern.matcher(text);
+ text = matcher.replaceAll(varDef.value());
+ });
+ return text;
+ } catch (Exception exc) {
+ throw exc;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/package-info.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/package-info.java
new file mode 100644
index 00000000..2a193f2f
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/package-info.java
@@ -0,0 +1,5 @@
+package net.hostsharing.hsadminng.rbac.rbacdef;
+
+// TODO: The whole code in this package is more like a quick hack to solve an urgent problem.
+// It should be re-written in PostgreSQL pl/pgsql,
+// so that no Java is needed to use this RBAC system in it's full extend.
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java
similarity index 89%
rename from src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java
rename to src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java
index 6dc8d1ce..f7b3cdf4 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantEntity.java
@@ -1,13 +1,13 @@
package net.hostsharing.hsadminng.rbac.rbacgrant;
import lombok.*;
-import org.jetbrains.annotations.NotNull;
import org.springframework.data.annotation.Immutable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
+import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.UUID;
@@ -20,7 +20,7 @@ import java.util.UUID;
@Immutable
@NoArgsConstructor
@AllArgsConstructor
-public class RawRbacGrantEntity {
+public class RawRbacGrantEntity implements Comparable {
@Id
private UUID uuid;
@@ -64,4 +64,9 @@ public class RawRbacGrantEntity {
// TODO: remove .distinct() once partner.person + partner.contact are removed
return roles.stream().map(RawRbacGrantEntity::toDisplay).sorted().distinct().toList();
}
+
+ @Override
+ public int compareTo(final Object o) {
+ return uuid.compareTo(((RawRbacGrantEntity)o).uuid);
+ }
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java
similarity index 67%
rename from src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java
rename to src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java
index c7ac60ab..37828bdf 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RawRbacGrantRepository.java
@@ -8,4 +8,8 @@ import java.util.UUID;
public interface RawRbacGrantRepository extends Repository {
List findAll();
+
+ List findByAscendingUuid(UUID ascendingUuid);
+
+ List findByDescendantUuid(UUID refUuid);
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
new file mode 100644
index 00000000..0296cd61
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
@@ -0,0 +1,206 @@
+package net.hostsharing.hsadminng.rbac.rbacgrant;
+
+import net.hostsharing.hsadminng.context.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import jakarta.validation.constraints.NotNull;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.joining;
+import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include.*;
+
+// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
+@Service
+public class RbacGrantsDiagramService {
+
+ public static void writeToFile(final String title, final String graph, final String fileName) {
+
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
+ writer.write("""
+ ### all grants to %s
+
+ ```mermaid
+ %s
+ ```
+ """.formatted(title, graph));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public enum Include {
+ DETAILS,
+ USERS,
+ PERMISSIONS,
+ NOT_ASSUMED,
+ TEST_ENTITIES,
+ NON_TEST_ENTITIES
+ }
+
+ @Autowired
+ private Context context;
+
+ @Autowired
+ private RawRbacGrantRepository rawGrantRepo;
+
+ @PersistenceContext
+ private EntityManager em;
+
+ public String allGrantsToCurrentUser(final EnumSet includes) {
+ final var graph = new HashSet();
+ for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
+ traverseGrantsTo(graph, subjectUuid, includes);
+ }
+ return toMermaidFlowchart(graph, includes);
+ }
+
+ private void traverseGrantsTo(final Set graph, final UUID refUuid, final EnumSet includes) {
+ final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
+ grants.forEach(g -> {
+ if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
+ return;
+ }
+ if ( !g.getDescendantIdName().startsWith("role global")) {
+ if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
+ return;
+ }
+ if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
+ return;
+ }
+ }
+ graph.add(g);
+ if (includes.contains(NOT_ASSUMED) || g.isAssumed()) {
+ traverseGrantsTo(graph, g.getDescendantUuid(), includes);
+ }
+ });
+ }
+
+ public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet includes) {
+ final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbacpermission WHERE objectuuid=:targetObject AND op=:op")
+ .setParameter("targetObject", targetObject)
+ .setParameter("op", op)
+ .getSingleResult();
+ final var graph = new HashSet();
+ traverseGrantsFrom(graph, refUuid, includes);
+ return toMermaidFlowchart(graph, includes);
+ }
+
+ private void traverseGrantsFrom(final Set graph, final UUID refUuid, final EnumSet option) {
+ final var grants = rawGrantRepo.findByDescendantUuid(refUuid);
+ grants.forEach(g -> {
+ if (!option.contains(USERS) && g.getAscendantIdName().startsWith("user ")) {
+ return;
+ }
+ graph.add(g);
+ if (option.contains(NOT_ASSUMED) || g.isAssumed()) {
+ traverseGrantsFrom(graph, g.getAscendingUuid(), option);
+ }
+ });
+ }
+
+ private String toMermaidFlowchart(final HashSet graph, final EnumSet includes) {
+ final var entities =
+ includes.contains(DETAILS)
+ ? graph.stream()
+ .flatMap(g -> Stream.of(
+ new Node(g.getAscendantIdName(), g.getAscendingUuid()),
+ new Node(g.getDescendantIdName(), g.getDescendantUuid()))
+ )
+ .collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
+ .entrySet().stream()
+ .map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
+ + entity.getValue().stream()
+ .map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
+ .sorted()
+ .distinct()
+ .collect(joining("\n\n ")))
+ .collect(joining("\n\nend\n\n"))
+ + "\n\nend\n\n"
+ : "";
+
+ final var grants = graph.stream()
+ .map(g -> quoted(g.getAscendantIdName())
+ + " -->" + (g.isAssumed() ? " " : "|XX| ")
+ + quoted(g.getDescendantIdName()))
+ .sorted()
+ .collect(joining("\n"));
+
+ final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
+ return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
+ + "flowchart TB\n\n"
+ + entities
+ + grants;
+ }
+
+ private String renderSubgraph(final String entityId) {
+ // this does not work according to Mermaid bug https://github.com/mermaid-js/mermaid/issues/3806
+ // if (entityId.contains("#")) {
+ // final var parts = entityId.split("#");
+ // final var table = parts[0];
+ // final var entity = parts[1];
+ // if (table.equals("entity")) {
+ // return "[" + entity "]";
+ // }
+ // return "[" + table + "\n" + entity + "]";
+ // }
+ return "[" + entityId + "]";
+ }
+
+ private static String renderEntityIdName(final Node node) {
+ final var refType = refType(node.idName());
+ if (refType.equals("user")) {
+ return "users";
+ }
+ if (refType.equals("perm")) {
+ return node.idName().split(" ", 4)[3];
+ }
+ if (refType.equals("role")) {
+ final var withoutRolePrefix = node.idName().substring("role:".length());
+ return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf('.'));
+ }
+ throw new IllegalArgumentException("unknown refType '" + refType + "' in '" + node.idName() + "'");
+ }
+
+ private String renderNode(final String idName, final UUID uuid) {
+ return quoted(idName) + renderNodeContent(idName, uuid);
+ }
+
+ private String renderNodeContent(final String idName, final UUID uuid) {
+ final var refType = refType(idName);
+
+ if (refType.equals("user")) {
+ final var displayName = idName.substring(refType.length()+1);
+ return "(" + displayName + "\nref:" + uuid + ")";
+ }
+ if (refType.equals("role")) {
+ final var roleType = idName.substring(idName.lastIndexOf('.') + 1);
+ return "[" + roleType + "\nref:" + uuid + "]";
+ }
+ if (refType.equals("perm")) {
+ final var roleType = idName.split(" ")[1];
+ return "{{" + roleType + "\nref:" + uuid + "}}";
+ }
+ return "";
+ }
+
+ private static String refType(final String idName) {
+ return idName.split(" ", 2)[0];
+ }
+
+ @NotNull
+ private static String quoted(final String idName) {
+ return idName.replace(" ", ":").replaceAll("@.*", "");
+ }
+}
+
+record Node(String idName, UUID uuid) {
+
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacobject/RbacObject.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacobject/RbacObject.java
new file mode 100644
index 00000000..4d7646d1
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacobject/RbacObject.java
@@ -0,0 +1,8 @@
+package net.hostsharing.hsadminng.rbac.rbacobject;
+
+
+import java.util.UUID;
+
+public interface RbacObject {
+ UUID getUuid();
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserPermission.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserPermission.java
index ba251885..f29503c3 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserPermission.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserPermission.java
@@ -8,8 +8,8 @@ public interface RbacUserPermission {
String getRoleName();
UUID getPermissionUuid();
String getOp();
+ String getOpTableName();
String getObjectTable();
String getObjectIdName();
UUID getObjectUuid();
-
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java
index 1bd000ba..67607c83 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerController.java
@@ -10,6 +10,8 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
import java.util.List;
@RestController
@@ -24,6 +26,9 @@ public class TestCustomerController implements TestCustomersApi {
@Autowired
private TestCustomerRepository testCustomerRepository;
+ @PersistenceContext
+ EntityManager em;
+
@Override
@Transactional(readOnly = true)
public ResponseEntity> listCustomers(
@@ -48,7 +53,6 @@ public class TestCustomerController implements TestCustomersApi {
context.define(currentUser, assumedRoles);
final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class));
-
final var uri =
MvcUriComponentsBuilder.fromController(getClass())
.path("/api/test/customers/{id}")
diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
index 1f2bb0e1..99b0fb3c 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
@@ -4,17 +4,27 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
+
@Entity
@Table(name = "test_customer_rv")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
-public class TestCustomerEntity {
+public class TestCustomerEntity implements HasUuid {
@Id
@GeneratedValue
@@ -25,4 +35,29 @@ public class TestCustomerEntity {
@Column(name = "adminusername")
private String adminUserName;
+
+ public static RbacView rbac() {
+ return rbacViewFor("customer", TestCustomerEntity.class)
+ .withIdentityView(SQL.projection("prefix"))
+ .withRestrictedViewOrderBy(SQL.expression("reference"))
+ .withUpdatableColumns("reference", "prefix", "adminUserName")
+ // TODO: do we want explicit specification of parent-independent insert permissions?
+ // .toRole("global", ADMIN).grantPermission("customer", INSERT)
+
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR).unassumed();
+ with.incomingSuperRole(GLOBAL, ADMIN).unassumed();
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.permission(UPDATE);
+ })
+ .createSubRole(TENANT, (with) -> {
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("113-test-customer-rbac");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
new file mode 100644
index 00000000..6a031df7
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
@@ -0,0 +1,73 @@
+package net.hostsharing.hsadminng.test.dom;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
+import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
+
+import jakarta.persistence.*;
+import java.io.IOException;
+import java.util.UUID;
+
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
+
+@Entity
+@Table(name = "test_domain_rv")
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class TestDomainEntity implements HasUuid {
+
+ @Id
+ @GeneratedValue
+ private UUID uuid;
+
+ @Version
+ private int version;
+
+ @ManyToOne(optional = false)
+ @JoinColumn(name = "packageuuid")
+ private TestPackageEntity pac;
+
+ private String name;
+
+ private String description;
+
+ public static RbacView rbac() {
+ return rbacViewFor("domain", TestDomainEntity.class)
+ .withIdentityView(SQL.projection("name"))
+ .withUpdatableColumns("version", "packageUuid", "description")
+
+ .importEntityAlias("package", TestPackageEntity.class,
+ dependsOnColumn("packageUuid"),
+ fetchedBySql("""
+ SELECT * FROM test_package p
+ WHERE p.uuid= ${ref}.packageUuid
+ """))
+ .toRole("package", ADMIN).grantPermission("domain", INSERT)
+
+ .createRole(OWNER, (with) -> {
+ with.incomingSuperRole("package", ADMIN);
+ with.outgoingSubRole("package", TENANT);
+ with.permission(DELETE);
+ with.permission(UPDATE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.outgoingSubRole("package", TENANT);
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("133-test-domain-rbac");
+ }
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
index 8687666f..757fcf05 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
@@ -4,18 +4,28 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
import jakarta.persistence.*;
+import java.io.IOException;
import java.util.UUID;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
+
@Entity
@Table(name = "test_package_rv")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
-public class TestPackageEntity {
+public class TestPackageEntity implements HasUuid {
@Id
@GeneratedValue
@@ -31,4 +41,34 @@ public class TestPackageEntity {
private String name;
private String description;
+
+
+ public static RbacView rbac() {
+ return rbacViewFor("package", TestPackageEntity.class)
+ .withIdentityView(SQL.projection("name"))
+ .withUpdatableColumns("version", "customerUuid", "description")
+
+ .importEntityAlias("customer", TestCustomerEntity.class,
+ dependsOnColumn("customerUuid"),
+ fetchedBySql("""
+ SELECT * FROM test_customer c
+ WHERE c.uuid= ${ref}.customerUuid
+ """))
+ .toRole("customer", ADMIN).grantPermission("package", INSERT)
+
+ .createRole(OWNER, (with) -> {
+ with.incomingSuperRole("customer", ADMIN);
+ with.permission(DELETE);
+ with.permission(UPDATE);
+ })
+ .createSubRole(ADMIN)
+ .createSubRole(TENANT, (with) -> {
+ with.outgoingSubRole("customer", TENANT);
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("123-test-package-rbac");
+ }
}
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql
index 4820cf9c..8de41891 100644
--- a/src/main/resources/db/changelog/010-context.sql
+++ b/src/main/resources/db/changelog/010-context.sql
@@ -23,22 +23,27 @@ end; $$;
Defines the transaction context.
*/
create or replace procedure defineContext(
- currentTask varchar,
- currentRequest varchar = null,
- currentUser varchar = null,
- assumedRoles varchar = null
+ currentTask varchar(96),
+ currentRequest text = null,
+ currentUser varchar(63) = null,
+ assumedRoles varchar(256) = null
)
language plpgsql as $$
begin
+ currentTask := coalesce(currentTask, '');
+ assert length(currentTask) <= 96, FORMAT('currentTask must not be longer than 96 characters: "%s"', currentTask);
+ assert length(currentTask) > 8, FORMAT('currentTask must be at least 8 characters long: "%s""', currentTask);
execute format('set local hsadminng.currentTask to %L', currentTask);
currentRequest := coalesce(currentRequest, '');
execute format('set local hsadminng.currentRequest to %L', currentRequest);
currentUser := coalesce(currentUser, '');
+ assert length(currentUser) <= 63, FORMAT('currentUser must not be longer than 63 characters: "%s"', currentUser);
execute format('set local hsadminng.currentUser to %L', currentUser);
assumedRoles := coalesce(assumedRoles, '');
+ assert length(assumedRoles) <= 256, FORMAT('assumedRoles must not be longer than 256 characters: "%s"', assumedRoles);
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
diff --git a/src/main/resources/db/changelog/020-audit-log.sql b/src/main/resources/db/changelog/020-audit-log.sql
index 173e5741..ec14ad0d 100644
--- a/src/main/resources/db/changelog/020-audit-log.sql
+++ b/src/main/resources/db/changelog/020-audit-log.sql
@@ -27,9 +27,9 @@ create table tx_context
txId bigint not null,
txTimestamp timestamp not null,
currentUser varchar(63) not null, -- not the uuid, because users can be deleted
- assumedRoles varchar not null, -- not the uuids, because roles can be deleted
+ assumedRoles varchar(256) not null, -- not the uuids, because roles can be deleted
currentTask varchar(96) not null,
- currentRequest varchar(512) not null
+ currentRequest text not null
);
create index on tx_context using brin (txTimestamp);
diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql
index fe2f30ae..2992d6a9 100644
--- a/src/main/resources/db/changelog/050-rbac-base.sql
+++ b/src/main/resources/db/changelog/050-rbac-base.sql
@@ -86,29 +86,6 @@ create or replace function findRbacUserId(userName varchar)
language sql as $$
select uuid from RbacUser where name = userName
$$;
-
-create type RbacWhenNotExists as enum ('fail', 'create');
-
-create or replace function getRbacUserId(userName varchar, whenNotExists RbacWhenNotExists)
- returns uuid
- returns null on null input
- language plpgsql as $$
-declare
- userUuid uuid;
-begin
- userUuid = findRbacUserId(userName);
- if (userUuid is null) then
- if (whenNotExists = 'fail') then
- raise exception 'RbacUser with name="%" not found', userName;
- end if;
- if (whenNotExists = 'create') then
- userUuid = createRbacUser(userName);
- end if;
- end if;
- return userUuid;
-end;
-$$;
-
--//
-- ============================================================================
@@ -203,15 +180,33 @@ create type RbacRoleDescriptor as
(
objectTable varchar(63), -- for human readability and easier debugging
objectUuid uuid,
- roleType RbacRoleType
+ roleType RbacRoleType,
+ assumed boolean
);
-create or replace function roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType)
+create or replace function assumed()
+ returns boolean
+ stable -- leakproof
+ language sql as $$
+ select true;
+$$;
+
+create or replace function unassumed()
+ returns boolean
+ stable -- leakproof
+ language sql as $$
+select false;
+$$;
+
+
+create or replace function roleDescriptor(
+ objectTable varchar(63), objectUuid uuid, roleType RbacRoleType,
+ assumed boolean = true) -- just for DSL readability, belongs actually to the grant
returns RbacRoleDescriptor
returns null on null input
stable -- leakproof
language sql as $$
-select objectTable, objectUuid, roleType::RbacRoleType;
+ select objectTable, objectUuid, roleType::RbacRoleType, assumed;
$$;
create or replace function createRole(roleDescriptor RbacRoleDescriptor)
@@ -275,21 +270,17 @@ create or replace function findRoleId(roleDescriptor RbacRoleDescriptor)
select uuid from RbacRole where objectUuid = roleDescriptor.objectUuid and roleType = roleDescriptor.roleType;
$$;
-create or replace function getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists)
+create or replace function getRoleId(roleDescriptor RbacRoleDescriptor)
returns uuid
- returns null on null input
language plpgsql as $$
declare
roleUuid uuid;
begin
- roleUuid = findRoleId(roleDescriptor);
+ assert roleDescriptor is not null, 'roleDescriptor must not be null';
+
+ roleUuid := findRoleId(roleDescriptor);
if (roleUuid is null) then
- if (whenNotExists = 'fail') then
- raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
- end if;
- if (whenNotExists = 'create') then
- roleUuid = createRole(roleDescriptor);
- end if;
+ raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
end if;
return roleUuid;
end;
@@ -365,38 +356,63 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
/*
*/
-create domain RbacOp as varchar(67)
+create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone
check (
- VALUE = '*'
- or VALUE = 'delete'
- or VALUE = 'edit'
- or VALUE = 'view'
- or VALUE = 'assume'
+ VALUE = 'DELETE'
+ or VALUE = 'UPDATE'
+ or VALUE = 'SELECT'
+ or VALUE = 'INSERT'
+ or VALUE = 'ASSUME'
+ -- TODO: all values below are deprecated, use insert with table
or VALUE ~ '^add-[a-z]+$'
or VALUE ~ '^new-[a-z-]+$'
);
create table RbacPermission
(
- uuid uuid primary key references RbacReference (uuid) on delete cascade,
- objectUuid uuid not null references RbacObject,
- op RbacOp not null,
+ uuid uuid primary key references RbacReference (uuid) on delete cascade,
+ objectUuid uuid not null references RbacObject,
+ op RbacOp not null,
+ opTableName varchar(60),
unique (objectUuid, op)
);
call create_journal('RbacPermission');
-create or replace function permissionExists(forObjectUuid uuid, forOp RbacOp)
- returns bool
- language sql as $$
-select exists(
- select op
- from RbacPermission p
- where p.objectUuid = forObjectUuid
- and p.op in ('*', forOp)
- );
-$$;
+create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
+ returns uuid
+ language plpgsql as $$
+declare
+ permissionUuid uuid;
+begin
+ if (forObjectUuid is null) then
+ raise exception 'forObjectUuid must not be null';
+ end if;
+ if (forOp = 'INSERT' and forOpTableName is null) then
+ raise exception 'INSERT permissions needs forOpTableName';
+ end if;
+ if (forOp <> 'INSERT' and forOpTableName is not null) then
+ raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
+ end if;
+ permissionUuid = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = forOp and opTableName = forOpTableName);
+ if (permissionUuid is null) then
+ insert into RbacReference ("type")
+ values ('RbacPermission')
+ returning uuid into permissionUuid;
+ begin
+ insert into RbacPermission (uuid, objectUuid, op, opTableName)
+ values (permissionUuid, forObjectUuid, forOp, forOpTableName);
+ exception
+ when others then
+ raise exception 'insert into RbacPermission (uuid, objectUuid, op, opTableName)
+ values (%, %, %, %);', permissionUuid, forObjectUuid, forOp, forOpTableName;
+ end;
+ end if;
+ return permissionUuid;
+end; $$;
+
+-- TODO: deprecated, remove and amend all usages to createPermission
create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[])
returns uuid[]
language plpgsql as $$
@@ -407,9 +423,6 @@ begin
if (forObjectUuid is null) then
raise exception 'forObjectUuid must not be null';
end if;
- if (array_length(permitOps, 1) > 1 and '*' = any (permitOps)) then
- raise exception '"*" operation must not be assigned along with other operations: %', permitOps;
- end if;
for i in array_lower(permitOps, 1)..array_upper(permitOps, 1)
loop
@@ -430,7 +443,19 @@ begin
end;
$$;
-create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp)
+create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
+ returns uuid
+ returns null on null input
+ stable -- leakproof
+ language sql as $$
+select uuid
+ from RbacPermission p
+ where p.objectUuid = forObjectUuid
+ and (forOp = 'SELECT' or p.op = forOp) -- all other RbacOp include 'SELECT'
+ and p.opTableName = forOpTableName
+$$;
+
+create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
returns uuid
returns null on null input
stable -- leakproof
@@ -439,23 +464,8 @@ select uuid
from RbacPermission p
where p.objectUuid = forObjectUuid
and p.op = forOp
+ and p.opTableName = forOpTableName
$$;
-
-create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp)
- returns uuid
- returns null on null input
- stable -- leakproof
- language plpgsql as $$
-declare
- permissionId uuid;
-begin
- permissionId := findPermissionId(forObjectUuid, forOp);
- if permissionId is null and forOp <> '*' then
- permissionId := findPermissionId(forObjectUuid, '*');
- end if;
- return permissionId;
-end $$;
-
--//
-- ============================================================================
@@ -552,6 +562,18 @@ select exists(
);
$$;
+create or replace function hasInsertPermission(objectUuid uuid, forOp RbacOp, tableName text )
+ returns BOOL
+ stable -- leakproof
+ language plpgsql as $$
+declare
+ permissionUuid uuid;
+begin
+ permissionUuid = findPermissionId(objectUuid, forOp, tableName);
+ return permissionUuid is not null;
+end;
+$$;
+
create or replace function hasGlobalRoleGranted(userUuid uuid)
returns bool
stable -- leakproof
@@ -566,6 +588,27 @@ select exists(
);
$$;
+create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid)
+ language plpgsql as $$
+begin
+ perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
+ perform assertReferenceType('permissionId (descendant)', permissionUuid, 'RbacPermission');
+
+ insert
+ into RbacGrants (grantedByTriggerOf, ascendantUuid, descendantUuid, assumed)
+ values (currentTriggerObjectUuid(), roleUuid, permissionUuid, true)
+ on conflict do nothing; -- allow granting multiple times
+end;
+$$;
+
+create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid)
+ language plpgsql as $$
+begin
+ call grantPermissionToRole(findRoleId(roleDesc), permissionUuid);
+end;
+$$;
+
+-- TODO: deprecated, remove and use grantPermissionToRole(...)
create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[])
language plpgsql as $$
begin
@@ -697,7 +740,7 @@ begin
select descendantUuid
from grants) as granted
join RbacPermission perm
- on granted.descendantUuid = perm.uuid and perm.op in ('*', requiredOp)
+ on granted.descendantUuid = perm.uuid and (requiredOp = 'SELECT' or perm.op = requiredOp)
join RbacObject obj on obj.uuid = perm.objectUuid and obj.objectTable = forObjectTable
limit maxObjects + 1;
@@ -789,6 +832,5 @@ do $$
create role restricted;
grant all privileges on all tables in schema public to restricted;
end if;
- end $$
+ end $$;
--//
-
diff --git a/src/main/resources/db/changelog/051-rbac-user-grant.sql b/src/main/resources/db/changelog/051-rbac-user-grant.sql
index 23dcbdd4..a82865c8 100644
--- a/src/main/resources/db/changelog/051-rbac-user-grant.sql
+++ b/src/main/resources/db/changelog/051-rbac-user-grant.sql
@@ -30,24 +30,35 @@ begin
insert
into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
values (grantedByRoleUuid, userUuid, roleUuid, doAssume);
- -- TODO.spec: What should happen on mupltiple grants? What if options (doAssume) are not the same?
+ -- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same?
-- Most powerful or latest grant wins? What about managed?
-- on conflict do nothing; -- allow granting multiple times
end; $$;
create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)
language plpgsql as $$
+declare
+ grantedByRoleIdName text;
+ grantedRoleIdName text;
begin
perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole');
perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser');
- if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then
- raise exception '[403] Access to granted-by-role % forbidden for %', grantedByRoleUuid, currentSubjects();
- end if;
+ assert grantedByRoleUuid is not null, 'grantedByRoleUuid must not be null';
+ assert grantedRoleUuid is not null, 'grantedRoleUuid must not be null';
+ assert userUuid is not null, 'userUuid must not be null';
+ if NOT isGranted(currentSubjectsUuids(), grantedByRoleUuid) then
+ select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName;
+ raise exception '[403] Access to granted-by-role % (%) forbidden for % (%)',
+ grantedByRoleIdName, grantedByRoleUuid, currentSubjects(), currentSubjectsUuids();
+ end if;
if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then
- raise exception '[403] Access to granted role % forbidden for %', grantedRoleUuid, currentSubjects();
+ select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName;
+ select roleIdName from rbacRole_ev where uuid=grantedRoleUuid into grantedRoleIdName;
+ raise exception '[403] Access to granted role % (%) forbidden for % (%)',
+ grantedRoleIdName, grantedRoleUuid, grantedByRoleIdName, grantedByRoleUuid;
end if;
insert
@@ -99,4 +110,17 @@ begin
where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid
and g.grantedByRoleUuid = revokeRoleFromUser.grantedByRoleUuid;
end; $$;
---/
+--//
+
+-- ============================================================================
+--changeset rbac-user-grant-REVOKE-PERMISSION-FROM-ROLE:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+create or replace procedure revokePermissionFromRole(permissionUuid uuid, superRoleUuid uuid)
+ language plpgsql as $$
+begin
+ raise INFO 'delete from RbacGrants where ascendantUuid = % and descendantUuid = %', superRoleUuid, permissionUuid;
+ delete from RbacGrants as g
+ where g.ascendantUuid = superRoleUuid and g.descendantUuid = permissionUuid;
+end; $$;
+--//
diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql
index b1757c56..b494d120 100644
--- a/src/main/resources/db/changelog/055-rbac-views.sql
+++ b/src/main/resources/db/changelog/055-rbac-views.sql
@@ -337,11 +337,9 @@ grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RES
/*
Returns all permissions granted to the given user,
which are also visible to the current user or assumed roles.
-
-
- */
-create or replace function grantedPermissions(targetUserUuid uuid)
- returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, objectTable varchar, objectIdName varchar, objectUuid uuid)
+*/
+create or replace function grantedPermissionsRaw(targetUserUuid uuid)
+ returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid uuid)
returns null on null input
language plpgsql as $$
declare
@@ -357,11 +355,13 @@ begin
return query select
xp.roleUuid,
(xp.roleObjectTable || '#' || xp.roleObjectIdName || '.' || xp.roleType) as roleName,
- xp.permissionUuid, xp.op, xp.permissionObjectTable, xp.permissionObjectIdName, xp.permissionObjectUuid
+ xp.permissionUuid, xp.op, xp.opTableName,
+ xp.permissionObjectTable, xp.permissionObjectIdName, xp.permissionObjectUuid
from (select
r.uuid as roleUuid, r.roletype, ro.objectTable as roleObjectTable,
findIdNameByObjectUuid(ro.objectTable, ro.uuid) as roleObjectIdName,
- p.uuid as permissionUuid, p.op, po.objecttable as permissionObjectTable,
+ p.uuid as permissionUuid, p.op, p.opTableName,
+ po.objecttable as permissionObjectTable,
findIdNameByObjectUuid(po.objectTable, po.uuid) as permissionObjectIdName,
po.uuid as permissionObjectUuid
from queryPermissionsGrantedToSubjectId( targetUserUuid) as p
@@ -373,4 +373,15 @@ begin
) xp;
-- @formatter:on
end; $$;
+
+create or replace function grantedPermissions(targetUserUuid uuid)
+ returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, opTableName varchar(60), objectTable varchar(60), objectIdName varchar, objectUuid uuid)
+ returns null on null input
+ language sql as $$
+ select * from grantedPermissionsRaw(targetUserUuid)
+ union all
+ select roleUuid, roleName, permissionUuid, 'SELECT'::RbacOp, opTableName, objectTable, objectIdName, objectUuid
+ from grantedPermissionsRaw(targetUserUuid)
+ where op <> 'SELECT'::RbacOp;
+$$;
--//
diff --git a/src/main/resources/db/changelog/057-rbac-role-builder.sql b/src/main/resources/db/changelog/057-rbac-role-builder.sql
index 81a81590..1a7da953 100644
--- a/src/main/resources/db/changelog/057-rbac-role-builder.sql
+++ b/src/main/resources/db/changelog/057-rbac-role-builder.sql
@@ -13,24 +13,6 @@ begin
return createPermissions(forObjectUuid, permitOps);
end; $$;
-create or replace function toRoleUuids(roleDescriptors RbacRoleDescriptor[])
- returns uuid[]
- language plpgsql
- strict as $$
-declare
- superRoleDescriptor RbacRoleDescriptor;
- superRoleUuids uuid[] := array []::uuid[];
-begin
- foreach superRoleDescriptor in array roleDescriptors
- loop
- if superRoleDescriptor is not null then
- superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail');
- end if;
- end loop;
-
- return superRoleUuids;
-end; $$;
-
-- =================================================================
-- CREATE ROLE
@@ -50,32 +32,37 @@ create or replace function createRoleWithGrants(
language plpgsql as $$
declare
roleUuid uuid;
- superRoleUuid uuid;
+ subRoleDesc RbacRoleDescriptor;
+ superRoleDesc RbacRoleDescriptor;
subRoleUuid uuid;
+ superRoleUuid uuid;
userUuid uuid;
grantedByRoleUuid uuid;
begin
roleUuid := createRole(roleDescriptor);
- if cardinality(permissions) >0 then
+ if cardinality(permissions) > 0 then
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
end if;
- foreach superRoleUuid in array toRoleUuids(incomingSuperRoles)
+ foreach superRoleDesc in array array_remove(incomingSuperRoles, null)
loop
- call grantRoleToRole(roleUuid, superRoleUuid);
+ superRoleUuid := getRoleId(superRoleDesc);
+ call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed);
end loop;
- foreach subRoleUuid in array toRoleUuids(outgoingSubRoles)
+ foreach subRoleDesc in array array_remove(outgoingSubRoles, null)
loop
- call grantRoleToRole(subRoleUuid, roleUuid);
+ subRoleUuid := getRoleId(subRoleDesc);
+ call grantRoleToRole(subRoleUuid, roleUuid, subRoleDesc.assumed);
end loop;
if cardinality(userUuids) > 0 then
if grantedByRole is null then
- raise exception 'to directly assign users to roles, grantingRole has to be given';
+ grantedByRoleUuid := roleUuid;
+ else
+ grantedByRoleUuid := getRoleId(grantedByRole);
end if;
- grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
foreach userUuid in array userUuids
loop
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql
index fa198308..89d585ea 100644
--- a/src/main/resources/db/changelog/058-rbac-generators.sql
+++ b/src/main/resources/db/changelog/058-rbac-generators.sql
@@ -13,8 +13,7 @@ declare
begin
createInsertTriggerSQL = format($sql$
create trigger createRbacObjectFor_%s_Trigger
- before insert
- on %s
+ before insert on %s
for each row
execute procedure insertRelatedRbacObject();
$sql$, targetTable, targetTable);
@@ -36,50 +35,50 @@ end; $$;
--changeset rbac-generators-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-create or replace procedure generateRbacRoleDescriptors(prefix text, targetTable text)
+create procedure generateRbacRoleDescriptors(prefix text, targetTable text)
language plpgsql as $$
declare
sql text;
begin
sql = format($sql$
- create or replace function %1$sOwner(entity %2$s)
+ create or replace function %1$sOwner(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'owner');
+ return roleDescriptor('%2$s', entity.uuid, 'owner', assumed);
end; $f$;
- create or replace function %1$sAdmin(entity %2$s)
+ create or replace function %1$sAdmin(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'admin');
+ return roleDescriptor('%2$s', entity.uuid, 'admin', assumed);
end; $f$;
- create or replace function %1$sAgent(entity %2$s)
+ create or replace function %1$sAgent(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'agent');
+ return roleDescriptor('%2$s', entity.uuid, 'agent', assumed);
end; $f$;
- create or replace function %1$sTenant(entity %2$s)
+ create or replace function %1$sTenant(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'tenant');
+ return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
end; $f$;
- create or replace function %1$sGuest(entity %2$s)
+ create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'guest');
+ return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
end; $f$;
$sql$, prefix, targetTable);
@@ -92,7 +91,7 @@ end; $$;
--changeset rbac-generators-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-create or replace procedure generateRbacIdentityView(targetTable text, idNameExpression text)
+create or replace procedure generateRbacIdentityViewFromQuery(targetTable text, sqlQuery text)
language plpgsql as $$
declare
sql text;
@@ -101,11 +100,9 @@ begin
-- create a view to the target main table which maps an idName to the objectUuid
sql = format($sql$
- create or replace view %1$s_iv as
- select target.uuid, cleanIdentifier(%2$s) as idName
- from %1$s as target;
+ create or replace view %1$s_iv as %2$s;
grant all privileges on %1$s_iv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
- $sql$, targetTable, idNameExpression);
+ $sql$, targetTable, sqlQuery);
execute sql;
-- creates a function which maps an idName to the objectUuid
@@ -130,6 +127,20 @@ begin
$sql$, targetTable);
execute sql;
end; $$;
+
+create or replace procedure generateRbacIdentityViewFromProjection(targetTable text, sqlProjection text)
+ language plpgsql as $$
+declare
+ sqlQuery text;
+begin
+ targettable := lower(targettable);
+
+ sqlQuery = format($sql$
+ select target.uuid, cleanIdentifier(%2$s) as idName
+ from %1$s as target;
+ $sql$, targetTable, sqlProjection);
+ call generateRbacIdentityViewFromQuery(targetTable, sqlQuery);
+end; $$;
--//
@@ -145,13 +156,13 @@ begin
targetTable := lower(targetTable);
/*
- Creates a restricted view based on the 'view' permission of the current subject.
+ Creates a restricted view based on the 'SELECT' permission of the current subject.
*/
sql := format($sql$
set session session authorization default;
create view %1$s_rv as
with accessibleObjects as (
- select queryAccessibleObjectUuidsOfSubjectIds('view', '%1$s', currentSubjectsUuids())
+ select queryAccessibleObjectUuidsOfSubjectIds('SELECT', '%1$s', currentSubjectsUuids())
)
select target.*
from %1$s as target
@@ -200,7 +211,7 @@ begin
returns trigger
language plpgsql as $f$
begin
- if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('delete', '%1$s', currentSubjectsUuids())) then
+ if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('DELETE', '%1$s', currentSubjectsUuids())) then
delete from %1$s p where p.uuid = old.uuid;
return old;
end if;
@@ -223,7 +234,7 @@ begin
/**
Instead of update trigger function for the restricted view
- based on the 'edit' permission of the current subject.
+ based on the 'UPDATE' permission of the current subject.
*/
if columnUpdates is not null then
sql := format($sql$
@@ -231,7 +242,7 @@ begin
returns trigger
language plpgsql as $f$
begin
- if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('edit', '%1$s', currentSubjectsUuids())) then
+ if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('UPDATE', '%1$s', currentSubjectsUuids())) then
update %1$s
set %2$s
where uuid = old.uuid;
diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql
index 034400fa..8313d05d 100644
--- a/src/main/resources/db/changelog/080-rbac-global.sql
+++ b/src/main/resources/db/changelog/080-rbac-global.sql
@@ -22,6 +22,19 @@ grant select on global to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
--//
+-- ============================================================================
+--changeset rbac-global-IS-GLOBAL-ADMIN:1 endDelimiter:--//
+-- ------------------------------------------------------------------
+
+create or replace function isGlobalAdmin()
+ returns boolean
+ language plpgsql as $$
+begin
+ return isGranted(currentSubjectsUuids(), findRoleId(globalAdmin()));
+end; $$;
+--//
+
+
-- ============================================================================
--changeset rbac-global-HAS-GLOBAL-PERMISSION:1 endDelimiter:--//
-- ------------------------------------------------------------------
@@ -96,12 +109,12 @@ commit;
/*
A global administrator role.
*/
-create or replace function globalAdmin()
+create or replace function globalAdmin(assumed boolean = true)
returns RbacRoleDescriptor
returns null on null input
stable -- leakproof
language sql as $$
-select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType;
+select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType, assumed;
$$;
begin transaction;
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.md b/src/main/resources/db/changelog/113-test-customer-rbac.md
new file mode 100644
index 00000000..7770e470
--- /dev/null
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.md
@@ -0,0 +1,43 @@
+### rbac customer
+
+This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.571772062.
+
+```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
+flowchart TB
+
+subgraph customer["`**customer**`"]
+ direction TB
+ style customer fill:#dd4901,stroke:#274d6e,stroke-width:8px
+
+ subgraph customer:roles[ ]
+ style customer:roles fill:#dd4901,stroke:white
+
+ role:customer:owner[[customer:owner]]
+ role:customer:admin[[customer:admin]]
+ role:customer:tenant[[customer:tenant]]
+ end
+
+ subgraph customer:permissions[ ]
+ style customer:permissions fill:#dd4901,stroke:white
+
+ perm:customer:DELETE{{customer:DELETE}}
+ perm:customer:UPDATE{{customer:UPDATE}}
+ perm:customer:SELECT{{customer:SELECT}}
+ end
+end
+
+%% granting roles to users
+user:creator ==>|XX| role:customer:owner
+
+%% granting roles to roles
+role:global:admin ==>|XX| role:customer:owner
+role:customer:owner ==> role:customer:admin
+role:customer:admin ==> role:customer:tenant
+
+%% granting permissions to roles
+role:customer:owner ==> perm:customer:DELETE
+role:customer:admin ==> perm:customer:UPDATE
+role:customer:tenant ==> perm:customer:SELECT
+
+```
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/113-test-customer-rbac.sql
index d7682cc1..6ae19710 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.sql
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql
@@ -1,4 +1,5 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
-- ============================================================================
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
@@ -15,82 +16,103 @@ call generateRbacRoleDescriptors('testCustomer', 'test_customer');
-- ============================================================================
---changeset test-customer-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset test-customer-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates the roles and their assignments for a new customer for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRolesForTestCustomer()
- returns trigger
- language plpgsql
- strict as $$
-declare
- testCustomerOwnerUuid uuid;
- customerAdminUuid uuid;
-begin
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
+create or replace procedure buildRbacSystemForTestCustomer(
+ NEW test_customer
+)
+ language plpgsql as $$
+declare
+
+begin
call enterTriggerForObjectUuid(NEW.uuid);
- -- the owner role with full access for Hostsharing administrators
- testCustomerOwnerUuid = createRoleWithGrants(
+ perform createRoleWithGrants(
testCustomerOwner(NEW),
- permissions => array['*'],
- incomingSuperRoles => array[globalAdmin()]
- );
+ permissions => array['DELETE'],
+ userUuids => array[currentUserUuid()],
+ incomingSuperRoles => array[globalAdmin(unassumed())]
+ );
- -- the admin role for the customer's admins, who can view and add products
- customerAdminUuid = createRoleWithGrants(
+ perform createRoleWithGrants(
testCustomerAdmin(NEW),
- permissions => array['view', 'add-package'],
- -- NO auto assume for customer owner to avoid exploding permissions for administrators
- userUuids => array[getRbacUserId(NEW.adminUserName, 'create')], -- implicitly ignored if null
- grantedByRole => globalAdmin()
- );
+ permissions => array['UPDATE'],
+ incomingSuperRoles => array[testCustomerOwner(NEW)]
+ );
- -- allow the customer owner role (thus administrators) to assume the customer admin role
- call grantRoleToRole(customerAdminUuid, testCustomerOwnerUuid, false);
-
- -- the tenant role which later can be used by owners+admins of sub-objects
perform createRoleWithGrants(
testCustomerTenant(NEW),
- permissions => array['view']
- );
+ permissions => array['SELECT'],
+ incomingSuperRoles => array[testCustomerAdmin(NEW)]
+ );
call leaveTriggerForObjectUuid(NEW.uuid);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new test_customer row.
*/
-drop trigger if exists createRbacRolesForTestCustomer_Trigger on test_customer;
-create trigger createRbacRolesForTestCustomer_Trigger
- after insert
- on test_customer
+create or replace function insertTriggerForTestCustomer_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call buildRbacSystemForTestCustomer(NEW);
+ return NEW;
+end; $$;
+
+create trigger insertTriggerForTestCustomer_tg
+ after insert on test_customer
for each row
-execute procedure createRbacRolesForTestCustomer();
+execute procedure insertTriggerForTestCustomer_tf();
+
--//
+-- ============================================================================
+--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+/**
+ Checks if the user or assumed roles are allowed to insert a row to test_customer.
+*/
+create or replace function test_customer_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into test_customer not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger test_customer_insert_permission_check_tg
+ before insert on test_customer
+ for each row
+ -- As there is no explicit INSERT grant specified for this table,
+ -- only global admins are allowed to insert any rows.
+ when ( not isGlobalAdmin() )
+ execute procedure test_customer_insert_permission_missing_tf();
+
+--//
-- ============================================================================
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('test_customer', $idName$
- target.prefix
+
+call generateRbacIdentityViewFromProjection('test_customer', $idName$
+ prefix
$idName$);
+
--//
-
-
-- ============================================================================
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('test_customer', 'target.prefix',
+call generateRbacRestrictedView('test_customer',
+ 'reference',
$updates$
reference = new.reference,
prefix = new.prefix,
@@ -99,47 +121,3 @@ call generateRbacRestrictedView('test_customer', 'target.prefix',
--//
--- ============================================================================
---changeset test-customer-rbac-ADD-CUSTOMER:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for add-customer and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global add-customer permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['add-customer']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addTestCustomerNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] add-customer not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
-end; $$;
-
-/**
- Checks if the user or assumed roles are allowed to add a new customer.
- */
-create trigger test_customer_insert_trigger
- before insert
- on test_customer
- for each row
- when ( not hasGlobalPermission('add-customer') )
-execute procedure addTestCustomerNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/118-test-customer-test-data.sql b/src/main/resources/db/changelog/118-test-customer-test-data.sql
index 353b8f59..85c34ac6 100644
--- a/src/main/resources/db/changelog/118-test-customer-test-data.sql
+++ b/src/main/resources/db/changelog/118-test-customer-test-data.sql
@@ -28,6 +28,8 @@ declare
currentTask varchar;
custRowId uuid;
custAdminName varchar;
+ custAdminUuid uuid;
+ newCust test_customer;
begin
currentTask = 'creating RBAC test customer #' || custReference || '/' || custPrefix;
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
@@ -35,10 +37,19 @@ begin
custRowId = uuid_generate_v4();
custAdminName = 'customer-admin@' || custPrefix || '.example.com';
+ custAdminUuid = createRbacUser(custAdminName);
insert
into test_customer (reference, prefix, adminUserName)
values (custReference, custPrefix, custAdminName);
+
+ select * into newCust
+ from test_customer where reference=custReference;
+ call grantRoleToUser(
+ getRoleId(testCustomerOwner(newCust)),
+ getRoleId(testCustomerAdmin(newCust)),
+ custAdminUuid,
+ true);
end; $$;
--//
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.md b/src/main/resources/db/changelog/123-test-package-rbac.md
new file mode 100644
index 00000000..78da4439
--- /dev/null
+++ b/src/main/resources/db/changelog/123-test-package-rbac.md
@@ -0,0 +1,59 @@
+### rbac package
+
+This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.624847792.
+
+```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
+flowchart TB
+
+subgraph package["`**package**`"]
+ direction TB
+ style package fill:#dd4901,stroke:#274d6e,stroke-width:8px
+
+ subgraph package:roles[ ]
+ style package:roles fill:#dd4901,stroke:white
+
+ role:package:owner[[package:owner]]
+ role:package:admin[[package:admin]]
+ role:package:tenant[[package:tenant]]
+ end
+
+ subgraph package:permissions[ ]
+ style package:permissions fill:#dd4901,stroke:white
+
+ perm:package:INSERT{{package:INSERT}}
+ perm:package:DELETE{{package:DELETE}}
+ perm:package:UPDATE{{package:UPDATE}}
+ perm:package:SELECT{{package:SELECT}}
+ end
+end
+
+subgraph customer["`**customer**`"]
+ direction TB
+ style customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ subgraph customer:roles[ ]
+ style customer:roles fill:#99bcdb,stroke:white
+
+ role:customer:owner[[customer:owner]]
+ role:customer:admin[[customer:admin]]
+ role:customer:tenant[[customer:tenant]]
+ end
+end
+
+%% granting roles to roles
+role:global:admin -.->|XX| role:customer:owner
+role:customer:owner -.-> role:customer:admin
+role:customer:admin -.-> role:customer:tenant
+role:customer:admin ==> role:package:owner
+role:package:owner ==> role:package:admin
+role:package:admin ==> role:package:tenant
+role:package:tenant ==> role:customer:tenant
+
+%% granting permissions to roles
+role:customer:admin ==> perm:package:INSERT
+role:package:owner ==> perm:package:DELETE
+role:package:owner ==> perm:package:UPDATE
+role:package:tenant ==> perm:package:SELECT
+
+```
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql
index 9e68468c..20562642 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.sql
+++ b/src/main/resources/db/changelog/123-test-package-rbac.sql
@@ -1,4 +1,5 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
-- ============================================================================
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
@@ -15,95 +16,211 @@ call generateRbacRoleDescriptors('testPackage', 'test_package');
-- ============================================================================
---changeset test-package-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset test-package-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
+
/*
- Creates the roles and their assignments for a new package for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRolesForTestPackage()
- returns trigger
- language plpgsql
- strict as $$
+
+create or replace procedure buildRbacSystemForTestPackage(
+ NEW test_package
+)
+ language plpgsql as $$
+
declare
- parentCustomer test_customer;
+ newCustomer test_customer;
+
begin
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
-
call enterTriggerForObjectUuid(NEW.uuid);
+ SELECT * FROM test_customer c
+ WHERE c.uuid= NEW.customerUuid
+ into newCustomer;
- select * from test_customer as c where c.uuid = NEW.customerUuid into parentCustomer;
-
- -- an owner role is created and assigned to the customer's admin role
perform createRoleWithGrants(
- testPackageOwner(NEW),
- permissions => array ['*'],
- incomingSuperRoles => array[testCustomerAdmin(parentCustomer)]
- );
+ testPackageOwner(NEW),
+ permissions => array['DELETE', 'UPDATE'],
+ incomingSuperRoles => array[testCustomerAdmin(newCustomer)]
+ );
- -- an owner role is created and assigned to the package owner role
perform createRoleWithGrants(
- testPackageAdmin(NEW),
- permissions => array ['add-domain'],
+ testPackageAdmin(NEW),
incomingSuperRoles => array[testPackageOwner(NEW)]
- );
+ );
- -- and a package tenant role is created and assigned to the package admin as well
perform createRoleWithGrants(
- testPackageTenant(NEW),
- permissions => array['view'],
- incomingsuperroles => array[testPackageAdmin(NEW)],
- outgoingSubRoles => array[testCustomerTenant(parentCustomer)]
- );
+ testPackageTenant(NEW),
+ permissions => array['SELECT'],
+ incomingSuperRoles => array[testPackageAdmin(NEW)],
+ outgoingSubRoles => array[testCustomerTenant(newCustomer)]
+ );
call leaveTriggerForObjectUuid(NEW.uuid);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new package.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new test_package row.
*/
-create trigger createRbacRolesForTestPackage_Trigger
- after insert
- on test_package
+create or replace function insertTriggerForTestPackage_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call buildRbacSystemForTestPackage(NEW);
+ return NEW;
+end; $$;
+
+create trigger insertTriggerForTestPackage_tg
+ after insert on test_package
for each row
-execute procedure createRbacRolesForTestPackage();
+execute procedure insertTriggerForTestPackage_tf();
+
--//
-
-- ============================================================================
---changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacIdentityView('test_package', 'target.name');
---//
-
-
--- ============================================================================
---changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates a view to the customer main table which maps the identifying name
- (in this case, the prefix) to the objectUuid.
+ Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/
--- drop view if exists test_package_rv;
--- create or replace view test_package_rv as
--- select target.*
--- from test_package as target
--- where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_package', currentSubjectsUuids()))
--- order by target.name;
--- grant all privileges on test_package_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
-call generateRbacRestrictedView('test_package', 'target.name',
- $updates$
- version = new.version,
- customerUuid = new.customerUuid,
- name = new.name,
- description = new.description
- $updates$);
+create or replace procedure updateRbacRulesForTestPackage(
+ OLD test_package,
+ NEW test_package
+)
+ language plpgsql as $$
+
+declare
+ oldCustomer test_customer;
+ newCustomer test_customer;
+
+begin
+ call enterTriggerForObjectUuid(NEW.uuid);
+
+ SELECT * FROM test_customer c
+ WHERE c.uuid= OLD.customerUuid
+ into oldCustomer;
+ SELECT * FROM test_customer c
+ WHERE c.uuid= NEW.customerUuid
+ into newCustomer;
+
+ if NEW.customerUuid <> OLD.customerUuid then
+
+ call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
+
+ call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
+ call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
+
+ call revokeRoleFromRole(testCustomerTenant(oldCustomer), testPackageTenant(OLD));
+ call grantRoleToRole(testCustomerTenant(newCustomer), testPackageTenant(NEW));
+
+ end if;
+
+ call leaveTriggerForObjectUuid(NEW.uuid);
+end; $$;
+
+/*
+ AFTER INSERT TRIGGER to re-wire the grant structure for a new test_package row.
+ */
+
+create or replace function updateTriggerForTestPackage_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call updateRbacRulesForTestPackage(OLD, NEW);
+ return NEW;
+end; $$;
+
+create trigger updateTriggerForTestPackage_tg
+ after update on test_package
+ for each row
+execute procedure updateTriggerForTestPackage_tf();
--//
+-- ============================================================================
+--changeset test-package-rbac-INSERT:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+/*
+ Creates INSERT INTO test_package permissions for the related test_customer rows.
+ */
+do language plpgsql $$
+ declare
+ row test_customer;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO test_package permissions for the related test_customer rows');
+
+ FOR row IN SELECT * FROM test_customer
+ LOOP
+ roleUuid := findRoleId(testCustomerAdmin(row));
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
+ call grantPermissionToRole(roleUuid, permissionUuid);
+ END LOOP;
+ END;
+$$;
+
+/**
+ Adds test_package INSERT permission to specified role of new test_customer rows.
+*/
+create or replace function test_package_test_customer_insert_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call grantPermissionToRole(
+ testCustomerAdmin(NEW),
+ createPermission(NEW.uuid, 'INSERT', 'test_package'));
+ return NEW;
+end; $$;
+
+create trigger test_package_test_customer_insert_tg
+ after insert on test_customer
+ for each row
+execute procedure test_package_test_customer_insert_tf();
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to test_package.
+*/
+create or replace function test_package_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into test_package not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger test_package_insert_permission_check_tg
+ before insert on test_package
+ for each row
+ when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
+ execute procedure test_package_insert_permission_missing_tf();
+
+--//
+-- ============================================================================
+--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+call generateRbacIdentityViewFromProjection('test_package', $idName$
+ name
+ $idName$);
+
+--//
+-- ============================================================================
+--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('test_package',
+ 'name',
+ $updates$
+ version = new.version,
+ customerUuid = new.customerUuid,
+ description = new.description
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/128-test-package-test-data.sql b/src/main/resources/db/changelog/128-test-package-test-data.sql
index 4667b742..9abba772 100644
--- a/src/main/resources/db/changelog/128-test-package-test-data.sql
+++ b/src/main/resources/db/changelog/128-test-package-test-data.sql
@@ -26,7 +26,7 @@ begin
custAdminUser = 'customer-admin@' || cust.prefix || '.example.com';
custAdminRole = 'test_customer#' || cust.prefix || '.admin';
- call defineContext(currentTask, null, custAdminUser, custAdminRole);
+ call defineContext(currentTask, null, 'superuser-fran@hostsharing.net', custAdminRole);
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
insert
@@ -35,7 +35,7 @@ begin
returning * into pac;
call grantRoleToUser(
- getRoleId(testCustomerAdmin(cust), 'fail'),
+ getRoleId(testCustomerAdmin(cust)),
findRoleId(testPackageAdmin(pac)),
createRbacUser('pac-admin-' || pacName || '@' || cust.prefix || '.example.com'),
true);
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.md b/src/main/resources/db/changelog/133-test-domain-rbac.md
new file mode 100644
index 00000000..bd5cf706
--- /dev/null
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.md
@@ -0,0 +1,88 @@
+### rbac domain
+
+This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.644658132.
+
+```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
+flowchart TB
+
+subgraph package.customer["`**package.customer**`"]
+ direction TB
+ style package.customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ subgraph package.customer:roles[ ]
+ style package.customer:roles fill:#99bcdb,stroke:white
+
+ role:package.customer:owner[[package.customer:owner]]
+ role:package.customer:admin[[package.customer:admin]]
+ role:package.customer:tenant[[package.customer:tenant]]
+ end
+end
+
+subgraph package["`**package**`"]
+ direction TB
+ style package fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ subgraph package.customer["`**package.customer**`"]
+ direction TB
+ style package.customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ subgraph package.customer:roles[ ]
+ style package.customer:roles fill:#99bcdb,stroke:white
+
+ role:package.customer:owner[[package.customer:owner]]
+ role:package.customer:admin[[package.customer:admin]]
+ role:package.customer:tenant[[package.customer:tenant]]
+ end
+ end
+
+ subgraph package:roles[ ]
+ style package:roles fill:#99bcdb,stroke:white
+
+ role:package:owner[[package:owner]]
+ role:package:admin[[package:admin]]
+ role:package:tenant[[package:tenant]]
+ end
+end
+
+subgraph domain["`**domain**`"]
+ direction TB
+ style domain fill:#dd4901,stroke:#274d6e,stroke-width:8px
+
+ subgraph domain:roles[ ]
+ style domain:roles fill:#dd4901,stroke:white
+
+ role:domain:owner[[domain:owner]]
+ role:domain:admin[[domain:admin]]
+ end
+
+ subgraph domain:permissions[ ]
+ style domain:permissions fill:#dd4901,stroke:white
+
+ perm:domain:INSERT{{domain:INSERT}}
+ perm:domain:DELETE{{domain:DELETE}}
+ perm:domain:UPDATE{{domain:UPDATE}}
+ perm:domain:SELECT{{domain:SELECT}}
+ end
+end
+
+%% granting roles to roles
+role:global:admin -.->|XX| role:package.customer:owner
+role:package.customer:owner -.-> role:package.customer:admin
+role:package.customer:admin -.-> role:package.customer:tenant
+role:package.customer:admin -.-> role:package:owner
+role:package:owner -.-> role:package:admin
+role:package:admin -.-> role:package:tenant
+role:package:tenant -.-> role:package.customer:tenant
+role:package:admin ==> role:domain:owner
+role:domain:owner ==> role:package:tenant
+role:domain:owner ==> role:domain:admin
+role:domain:admin ==> role:package:tenant
+
+%% granting permissions to roles
+role:package:admin ==> perm:domain:INSERT
+role:domain:owner ==> perm:domain:DELETE
+role:domain:owner ==> perm:domain:UPDATE
+role:domain:admin ==> perm:domain:SELECT
+
+```
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql
index a78bfb5f..e686dada 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.sql
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql
@@ -1,4 +1,5 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
-- ============================================================================
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
@@ -11,107 +12,214 @@ call generateRelatedRbacObject('test_domain');
--changeset test-domain-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRoleDescriptors('testDomain', 'test_domain');
-
-create or replace function createTestDomainTenantRoleIfNotExists(domain test_domain)
- returns uuid
- returns null on null input
- language plpgsql as $$
-declare
- domainTenantRoleDesc RbacRoleDescriptor;
- domainTenantRoleUuid uuid;
-begin
- domainTenantRoleDesc = testdomainTenant(domain);
- domainTenantRoleUuid = findRoleId(domainTenantRoleDesc);
- if domainTenantRoleUuid is not null then
- return domainTenantRoleUuid;
- end if;
-
- return createRoleWithGrants(
- domainTenantRoleDesc,
- permissions => array['view'],
- incomingSuperRoles => array[testdomainAdmin(domain)]
- );
-end; $$;
--//
-- ============================================================================
---changeset test-domain-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset test-domain-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
+
/*
- Creates the roles and their assignments for a new domain for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRulesForTestDomain()
+create or replace procedure buildRbacSystemForTestDomain(
+ NEW test_domain
+)
+ language plpgsql as $$
+
+declare
+ newPackage test_package;
+
+begin
+ call enterTriggerForObjectUuid(NEW.uuid);
+ SELECT * FROM test_package p
+ WHERE p.uuid= NEW.packageUuid
+ into newPackage;
+
+ perform createRoleWithGrants(
+ testDomainOwner(NEW),
+ permissions => array['DELETE', 'UPDATE'],
+ incomingSuperRoles => array[testPackageAdmin(newPackage)],
+ outgoingSubRoles => array[testPackageTenant(newPackage)]
+ );
+
+ perform createRoleWithGrants(
+ testDomainAdmin(NEW),
+ permissions => array['SELECT'],
+ incomingSuperRoles => array[testDomainOwner(NEW)],
+ outgoingSubRoles => array[testPackageTenant(newPackage)]
+ );
+
+ call leaveTriggerForObjectUuid(NEW.uuid);
+end; $$;
+
+/*
+ AFTER INSERT TRIGGER to create the role+grant structure for a new test_domain row.
+ */
+
+create or replace function insertTriggerForTestDomain_tf()
returns trigger
language plpgsql
strict as $$
-declare
- parentPackage test_package;
begin
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
-
- call enterTriggerForObjectUuid(NEW.uuid);
-
- select * from test_package where uuid = NEW.packageUuid into parentPackage;
-
- -- an owner role is created and assigned to the package's admin group
- perform createRoleWithGrants(
- testDomainOwner(NEW),
- permissions => array['*'],
- incomingSuperRoles => array[testPackageAdmin(parentPackage)]
- );
-
- -- and a domain admin role is created and assigned to the domain owner as well
- perform createRoleWithGrants(
- testDomainAdmin(NEW),
- permissions => array['edit'],
- incomingSuperRoles => array[testDomainOwner(NEW)],
- outgoingSubRoles => array[testPackageTenant(parentPackage)]
- );
-
- -- a tenent role is only created on demand
-
- call leaveTriggerForObjectUuid(NEW.uuid);
+ call buildRbacSystemForTestDomain(NEW);
return NEW;
end; $$;
-
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new domain.
- */
-drop trigger if exists createRbacRulesForTestDomain_Trigger on test_domain;
-create trigger createRbacRulesForTestDomain_Trigger
- after insert
- on test_domain
+create trigger insertTriggerForTestDomain_tg
+ after insert on test_domain
for each row
-execute procedure createRbacRulesForTestDomain();
+execute procedure insertTriggerForTestDomain_tf();
+
--//
+-- ============================================================================
+--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+/*
+ Called from the AFTER UPDATE TRIGGER to re-wire the grants.
+ */
+
+create or replace procedure updateRbacRulesForTestDomain(
+ OLD test_domain,
+ NEW test_domain
+)
+ language plpgsql as $$
+
+declare
+ oldPackage test_package;
+ newPackage test_package;
+
+begin
+ call enterTriggerForObjectUuid(NEW.uuid);
+
+ SELECT * FROM test_package p
+ WHERE p.uuid= OLD.packageUuid
+ into oldPackage;
+ SELECT * FROM test_package p
+ WHERE p.uuid= NEW.packageUuid
+ into newPackage;
+
+ if NEW.packageUuid <> OLD.packageUuid then
+
+ call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
+
+ call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
+ call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
+
+ call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainOwner(OLD));
+ call grantRoleToRole(testPackageTenant(newPackage), testDomainOwner(NEW));
+
+ call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainAdmin(OLD));
+ call grantRoleToRole(testPackageTenant(newPackage), testDomainAdmin(NEW));
+
+ end if;
+
+ call leaveTriggerForObjectUuid(NEW.uuid);
+end; $$;
+
+/*
+ AFTER INSERT TRIGGER to re-wire the grant structure for a new test_domain row.
+ */
+
+create or replace function updateTriggerForTestDomain_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call updateRbacRulesForTestDomain(OLD, NEW);
+ return NEW;
+end; $$;
+
+create trigger updateTriggerForTestDomain_tg
+ after update on test_domain
+ for each row
+execute procedure updateTriggerForTestDomain_tf();
+
+--//
+
+-- ============================================================================
+--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+/*
+ Creates INSERT INTO test_domain permissions for the related test_package rows.
+ */
+do language plpgsql $$
+ declare
+ row test_package;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO test_domain permissions for the related test_package rows');
+
+ FOR row IN SELECT * FROM test_package
+ LOOP
+ roleUuid := findRoleId(testPackageAdmin(row));
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
+ call grantPermissionToRole(roleUuid, permissionUuid);
+ END LOOP;
+ END;
+$$;
+
+/**
+ Adds test_domain INSERT permission to specified role of new test_package rows.
+*/
+create or replace function test_domain_test_package_insert_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call grantPermissionToRole(
+ testPackageAdmin(NEW),
+ createPermission(NEW.uuid, 'INSERT', 'test_domain'));
+ return NEW;
+end; $$;
+
+create trigger test_domain_test_package_insert_tg
+ after insert on test_package
+ for each row
+execute procedure test_domain_test_package_insert_tf();
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to test_domain.
+*/
+create or replace function test_domain_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into test_domain not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger test_domain_insert_permission_check_tg
+ before insert on test_domain
+ for each row
+ when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
+ execute procedure test_domain_insert_permission_missing_tf();
+
+--//
-- ============================================================================
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('test_domain', $idName$
- target.name
+
+call generateRbacIdentityViewFromProjection('test_domain', $idName$
+ name
$idName$);
+
--//
-
-
-- ============================================================================
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-
-/*
- Creates a view to the customer main table which maps the identifying name
- (in this case, the prefix) to the objectUuid.
- */
-drop view if exists test_domain_rv;
-create or replace view test_domain_rv as
-select target.*
- from test_domain as target
- where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectsUuids()));
-grant all privileges on test_domain_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
+call generateRbacRestrictedView('test_domain',
+ 'name',
+ $updates$
+ version = new.version,
+ packageUuid = new.packageUuid,
+ description = new.description
+ $updates$);
--//
+
+
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
index 7ba7891b..3a9b0c34 100644
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
@@ -33,7 +33,7 @@ begin
perform createRoleWithGrants(
hsOfficeContactOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
@@ -41,7 +41,7 @@ begin
perform createRoleWithGrants(
hsOfficeContactAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
);
@@ -52,7 +52,7 @@ begin
perform createRoleWithGrants(
hsOfficeContactGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
);
@@ -75,7 +75,7 @@ execute procedure createRbacRolesForHsOfficeContact();
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_contact', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$
target.label
$idName$);
--//
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
index 42eacf2f..fbb1f8e1 100644
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
@@ -31,16 +31,16 @@ begin
perform createRoleWithGrants(
hsOfficePersonOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
);
- -- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to edit the data?
+ -- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
perform createRoleWithGrants(
hsOfficePersonAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
);
@@ -51,7 +51,7 @@ begin
perform createRoleWithGrants(
hsOfficePersonGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
);
@@ -73,7 +73,7 @@ execute procedure createRbacRolesForHsOfficePerson();
-- ============================================================================
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_person', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_person', $idName$
concat(target.tradeName, target.familyName, target.givenName)
$idName$);
--//
diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md
index c41de32c..8ffa55ff 100644
--- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md
+++ b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md
@@ -42,151 +42,3 @@ subgraph hsOfficeRelationship
end
```
- if TG_OP = 'INSERT' then
-
- -- the owner role with full access for admins of the relAnchor global admins
- ownerRole = createRole(
- hsOfficeRelationshipOwner(NEW),
- grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
- beneathRoles(array[
- globalAdmin(),
- hsOfficePersonAdmin(newRelAnchor)])
- );
-
- -- the admin role with full access for the owner
- adminRole = createRole(
- hsOfficeRelationshipAdmin(NEW),
- grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
- beneathRole(ownerRole)
- );
-
- -- the tenant role for those related users who can view the data
- perform createRole(
- hsOfficeRelationshipTenant,
- grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
- beneathRoles(array[
- hsOfficePersonAdmin(newRelAnchor),
- hsOfficePersonAdmin(newRelHolder),
- hsOfficeContactAdmin(newContact)]),
- withSubRoles(array[
- hsOfficePersonTenant(newRelAnchor),
- hsOfficePersonTenant(newRelHolder),
- hsOfficeContactTenant(newContact)])
- );
-
- -- anchor and holder admin roles need each others tenant role
- -- to be able to see the joined relationship
- call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
- call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
- call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
-
- elsif TG_OP = 'UPDATE' then
-
- if OLD.contactUuid <> NEW.contactUuid then
- -- nothing but the contact can be updated,
- -- in other cases, a new relationship needs to be created and the old updated
-
- select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
-
- call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
- call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
-
- call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
- call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
- end if;
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
-
- return NEW;
-end; $$;
-
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-create trigger createRbacRolesForHsOfficeRelationship_Trigger
- after insert
- on hs_office_relationship
- for each row
-execute procedure hsOfficeRelationshipRbacRolesTrigger();
-
-/*
- An AFTER UPDATE TRIGGER which updates the role structure of a customer.
- */
-create trigger updateRbacRolesForHsOfficeRelationship_Trigger
- after update
- on hs_office_relationship
- for each row
-execute procedure hsOfficeRelationshipRbacRolesTrigger();
---//
-
-
--- ============================================================================
---changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_relationship', $idName$
- (select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
- || '-with-' || target.relType || '-' ||
- (select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
- $idName$);
---//
-
-
--- ============================================================================
---changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_relationship',
- '(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
- $updates$
- contactUuid = new.contactUuid
- $updates$);
---//
-
--- TODO: exception if one tries to amend any other column
-
-
--- ============================================================================
---changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-relationship and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-relationship permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-relationship not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
-end; $$;
-
-/**
- Checks if the user or assumed roles are allowed to create a new customer.
- */
-create trigger hs_office_relationship_insert_trigger
- before insert
- on hs_office_relationship
- for each row
- -- TODO.spec: who is allowed to create new relationships
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql
index 928af48c..126664a4 100644
--- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql
+++ b/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql
@@ -45,7 +45,7 @@ begin
perform createRoleWithGrants(
hsOfficeRelationshipOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[
globalAdmin(),
hsOfficePersonAdmin(newRelAnchor)]
@@ -53,14 +53,14 @@ begin
perform createRoleWithGrants(
hsOfficeRelationshipAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
);
-- the tenant role for those related users who can view the data
perform createRoleWithGrants(
hsOfficeRelationshipTenant,
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeRelationshipAdmin(NEW),
hsOfficePersonAdmin(newRelAnchor),
@@ -124,7 +124,7 @@ execute procedure hsOfficeRelationshipRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_relationship', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|| '-with-' || target.relType || '-' ||
(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
index 4b4da009..d16048fd 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
@@ -48,13 +48,13 @@ begin
perform createRoleWithGrants(
hsOfficePartnerOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficePartnerAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[
@@ -84,7 +84,7 @@ begin
perform createRoleWithGrants(
hsOfficePartnerGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
);
@@ -98,21 +98,21 @@ begin
--Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
call grantPermissionsToRole(
- getRoleId(hsOfficePartnerOwner(NEW), 'fail'),
- createPermissions(NEW.detailsUuid, array ['*'])
+ getRoleId(hsOfficePartnerOwner(NEW)),
+ createPermissions(NEW.detailsUuid, array ['DELETE'])
);
call grantPermissionsToRole(
- getRoleId(hsOfficePartnerAdmin(NEW), 'fail'),
- createPermissions(NEW.detailsUuid, array ['edit'])
+ getRoleId(hsOfficePartnerAdmin(NEW)),
+ createPermissions(NEW.detailsUuid, array ['UPDATE'])
);
call grantPermissionsToRole(
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
-- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
-- Otherwise package-admins etc. would be able to read the data.
- getRoleId(hsOfficePartnerAgent(NEW), 'fail'),
- createPermissions(NEW.detailsUuid, array ['view'])
+ getRoleId(hsOfficePartnerAgent(NEW)),
+ createPermissions(NEW.detailsUuid, array ['SELECT'])
);
@@ -187,7 +187,7 @@ execute procedure hsOfficePartnerRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_partner', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
partnerNumber || ':' ||
(select idName from hs_office_person_iv p where p.uuid = target.personuuid)
|| '-' ||
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
index ab94481e..c4e053b9 100644
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
@@ -7,13 +7,10 @@ call generateRelatedRbacObject('hs_office_partner_details');
--//
-
-
-
-- ============================================================================
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_partner_details', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_partner_details', $idName$
(select idName || '-details' from hs_office_partner_iv partner_iv
join hs_office_partner partner on (partner_iv.uuid = partner.uuid)
where partner.detailsUuid = target.uuid)
@@ -38,7 +35,7 @@ call generateRbacRestrictedView('hs_office_partner_details',
-- ============================================================================
---changeset hs-office-partner-details-rbac-NEW-CONTACT:1 endDelimiter:--//
+--changeset hs-office-partner-details-rbac-NEW-PARTNER-DETAILS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
index fc34f147..b2cee782 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
@@ -4,14 +4,14 @@
flowchart TB
subgraph global
- style hsOfficeBankAccount fill: #e9f7ef
+ style global fill: lightgray
role:global.admin[global.admin]
end
subgraph hsOfficeBankAccount
direction TB
- style hsOfficeBankAccount fill: #e9f7ef
+ style hsOfficeBankAccount fill: lightgreen
user:hsOfficeBankAccount.creator([bankAccount.creator])
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
index 148e0ee2..93b605ce 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
@@ -33,7 +33,7 @@ begin
perform createRoleWithGrants(
hsOfficeBankAccountOwner(NEW),
- permissions => array['delete'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
@@ -51,7 +51,7 @@ begin
perform createRoleWithGrants(
hsOfficeBankAccountGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
);
@@ -74,7 +74,7 @@ execute procedure createRbacRolesForHsOfficeBankAccount();
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_bankaccount', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$
target.holder
$idName$);
--//
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
index 02895c48..da7887cd 100644
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
@@ -41,13 +41,13 @@ begin
perform createRoleWithGrants(
hsOfficeSepaMandateOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficeSepaMandateAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
);
@@ -66,7 +66,7 @@ begin
perform createRoleWithGrants(
hsOfficeSepaMandateGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
);
@@ -94,7 +94,7 @@ execute procedure hsOfficeSepaMandateRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_sepamandate', idNameExpression => 'target.reference');
+call generateRbacIdentityViewFromProjection('hs_office_sepamandate', 'target.reference');
--//
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
index 30573125..5f684f49 100644
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
@@ -49,7 +49,7 @@ begin
perform createRoleWithGrants(
hsOfficeDebitorOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
@@ -57,7 +57,7 @@ begin
perform createRoleWithGrants(
hsOfficeDebitorAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
);
@@ -85,7 +85,7 @@ begin
perform createRoleWithGrants(
hsOfficeDebitorGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[
hsOfficeDebitorTenant(NEW)]
);
@@ -173,7 +173,7 @@ execute procedure hsOfficeDebitorRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_debitor', $idName$
+call generateRbacIdentityViewFromProjection('hs_office_debitor', $idName$
'#' ||
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
to_char(debitorNumberSuffix, 'fm00') ||
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
index 949f939c..2a4a4a50 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
@@ -41,13 +41,13 @@ begin
perform createRoleWithGrants(
hsOfficeMembershipOwner(NEW),
- permissions => array['*'],
+ permissions => array['DELETE'],
incomingSuperRoles => array[globalAdmin()]
);
perform createRoleWithGrants(
hsOfficeMembershipAdmin(NEW),
- permissions => array['edit'],
+ permissions => array['UPDATE'],
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
);
@@ -65,7 +65,7 @@ begin
perform createRoleWithGrants(
hsOfficeMembershipGuest(NEW),
- permissions => array['view'],
+ permissions => array['SELECT'],
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
);
@@ -93,7 +93,7 @@ execute procedure hsOfficeMembershipRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$
+call generateRbacIdentityViewFromProjection('hs_office_membership', $idName$
'#' ||
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
memberNumberSuffix ||
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
index dd465d9f..5ee8bfbe 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
@@ -42,8 +42,8 @@ begin
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
- createPermissions(NEW.uuid, array ['view'])
+ getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
+ createPermissions(NEW.uuid, array ['SELECT'])
);
else
@@ -68,8 +68,7 @@ execute procedure hsOfficeCoopSharesTransactionRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-coopSharesTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_coopSharesTransaction',
- idNameExpression => 'target.reference');
+call generateRbacIdentityViewFromProjection('hs_office_coopSharesTransaction', 'target.reference');
--//
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
index ac65c141..69920385 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
@@ -42,8 +42,8 @@ begin
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
- createPermissions(NEW.uuid, array ['view'])
+ getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
+ createPermissions(NEW.uuid, array ['SELECT'])
);
else
@@ -68,8 +68,7 @@ execute procedure hsOfficeCoopAssetsTransactionRbacRolesTrigger();
-- ============================================================================
--changeset hs-office-coopAssetsTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityView('hs_office_coopAssetsTransaction',
- idNameExpression => 'target.reference');
+call generateRbacIdentityViewFromProjection('hs_office_coopAssetsTransaction', 'target.reference');
--//
diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
index fe50ccf1..013b2309 100644
--- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
@@ -28,6 +28,7 @@ public class ArchitectureTest {
"..test",
"..test.cust",
"..test.pac",
+ "..test.dom",
"..context",
"..generated..",
"..persistence..",
@@ -49,6 +50,8 @@ public class ArchitectureTest {
"..rbac.rbacuser",
"..rbac.rbacgrant",
"..rbac.rbacrole",
+ "..rbac.rbacobject",
+ "..rbac.rbacdef",
"..stringify"
// ATTENTION: Don't simply add packages here, also add arch rules for the new package!
);
@@ -116,7 +119,10 @@ public class ArchitectureTest {
public static final ArchRule hsAdminPackagesRule = classes()
.that().resideInAPackage("..hs.office.(*)..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.(*)..");
+ .resideInAnyPackage(
+ "..hs.office.(*)..",
+ "..rbac.rbacgrant" // TODO: just because of RbacGrantsDiagramServiceIntegrationTest
+ );
@ArchTest
@SuppressWarnings("unused")
diff --git a/src/test/java/net/hostsharing/hsadminng/context/ContextBasedTest.java b/src/test/java/net/hostsharing/hsadminng/context/ContextBasedTest.java
index 1069fa5f..7f08f044 100644
--- a/src/test/java/net/hostsharing/hsadminng/context/ContextBasedTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/context/ContextBasedTest.java
@@ -1,14 +1,37 @@
package net.hostsharing.hsadminng.context;
+import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Import;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+
+@Import(RbacGrantsDiagramService.class)
public abstract class ContextBasedTest {
@Autowired
protected Context context;
+ @PersistenceContext
+ protected EntityManager em; // just to be used in subclasses
+
+ /**
+ * To generate a flowchart diagram from the database use something like this in a defined context:
+
+
+ RbacGrantsDiagramService.writeToFile(
+ "title",
+ diagramService.allGrantsToCurrentUser(of(RbacGrantsDiagramService.Include.USERS, RbacGrantsDiagramService.Include.TEST_ENTITIES, RbacGrantsDiagramService.Include.NOT_ASSUMED, RbacGrantsDiagramService.Include.DETAILS, RbacGrantsDiagramService.Include.PERMISSIONS)),
+ "filename.md
+ );
+
+ */
+ @Autowired
+ protected RbacGrantsDiagramService diagramService; // just to be used in subclasses
+
TestInfo test;
@BeforeEach
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
index f2847290..eb14e634 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
@@ -109,7 +109,7 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm delete on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
+ "{ grant perm DELETE on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
"{ grant role hs_office_bankaccount#sometempaccC.owner to role global#global.admin by system and assume }",
"{ grant role hs_office_bankaccount#sometempaccC.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }",
@@ -117,7 +117,7 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
"{ grant role hs_office_bankaccount#sometempaccC.tenant to role hs_office_bankaccount#sometempaccC.admin by system and assume }",
- "{ grant perm view on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.guest by system and assume }",
+ "{ grant perm SELECT on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.guest by system and assume }",
"{ grant role hs_office_bankaccount#sometempaccC.guest to role hs_office_bankaccount#sometempaccC.tenant by system and assume }",
null
));
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
index a78b761e..91ee8bde 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
@@ -111,11 +111,11 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialGrantNames,
"{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
- "{ grant perm edit on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
+ "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.tenant to role hs_office_contact#anothernewcontact.admin by system and assume }",
- "{ grant perm * on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
+ "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
- "{ grant perm view on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.guest by system and assume }",
+ "{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.guest by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.guest to role hs_office_contact#anothernewcontact.tenant by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
));
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
index f18447df..1f6964b8 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
@@ -114,7 +114,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm view on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
+ "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
null));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
index 20602661..609e7940 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
@@ -113,7 +113,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm view on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
+ "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
null));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
index 839039a2..0616e338 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
@@ -145,8 +145,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
}
@Nested
- @Accepts({ "Debitor:C(Create)" })
- class CreateDebitor {
+ class AddDebitor {
@Test
void globalAdmin_withoutAssumedRole_canAddDebitorWithBankAccount() {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
index c703c31a..46d0878f 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
@@ -118,8 +118,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
});
// then
- System.out.println("ok");
-// result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
+ result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
}
@Test
@@ -167,12 +166,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// owner
- "{ grant perm * on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
+ "{ grant perm DELETE on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
"{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }",
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
// admin
- "{ grant perm edit on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
+ "{ grant perm UPDATE on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
"{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
// agent
@@ -187,7 +186,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
// guest
- "{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
+ "{ grant perm SELECT on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
"{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
null));
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
index 6a0cd485..4483304a 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
@@ -126,11 +126,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
initialGrantNames,
// owner
- "{ grant perm * on membership#1000117:First to role membership#1000117:First.owner by system and assume }",
+ "{ grant perm DELETE on membership#1000117:First to role membership#1000117:First.owner by system and assume }",
"{ grant role membership#1000117:First.owner to role global#global.admin by system and assume }",
// admin
- "{ grant perm edit on membership#1000117:First to role membership#1000117:First.admin by system and assume }",
+ "{ grant perm UPDATE on membership#1000117:First to role membership#1000117:First.admin by system and assume }",
"{ grant role membership#1000117:First.admin to role membership#1000117:First.owner by system and assume }",
// agent
@@ -149,7 +149,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
"{ grant role membership#1000117:First.tenant to role partner#10001:First.agent by system and assume }",
// guest
- "{ grant perm view on membership#1000117:First to role membership#1000117:First.guest by system and assume }",
+ "{ grant perm SELECT on membership#1000117:First to role membership#1000117:First.guest by system and assume }",
"{ grant role membership#1000117:First.guest to role membership#1000117:First.tenant by system and assume }",
"{ grant role membership#1000117:First.guest to role partner#10001:First.tenant by system and assume }",
"{ grant role membership#1000117:First.guest to role debitor#1000111:First.tenant by system and assume }",
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index 325317b2..929aa919 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -21,7 +21,7 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.test.JpaAttempt;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
@@ -520,7 +520,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
- private void persist(final Integer id, final HasUuid entity) {
+ private void persist(final Integer id, final RbacObject entity) {
try {
//System.out.println("persisting #" + entity.hashCode() + ": " + entity);
em.persist(entity);
@@ -591,7 +591,7 @@ public class ImportOfficeData extends ContextBasedTest {
}).assertSuccessful();
}
- private void updateLegacyIds(
+ private void updateLegacyIds(
Map entities,
final String legacyIdTable,
final String legacyIdColumn) {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
index 2512a07d..94d06a77 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
@@ -171,29 +171,29 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
- "{ grant perm edit on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm UPDATE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm * on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
+ "{ grant perm DELETE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.admin to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant perm view on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant perm SELECT on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role contact#4th.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role person#EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role person#HostsharingeG.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
// owner
- "{ grant perm * on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }",
- "{ grant perm * on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.owner by system and assume }",
+ "{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }",
+ "{ grant perm DELETE on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.owner by system and assume }",
"{ grant role partner#20032:EBess-4th.owner to role global#global.admin by system and assume }",
// admin
- "{ grant perm edit on partner#20032:EBess-4th to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant perm edit on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.admin by system and assume }",
+ "{ grant perm UPDATE on partner#20032:EBess-4th to role partner#20032:EBess-4th.admin by system and assume }",
+ "{ grant perm UPDATE on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.admin by system and assume }",
"{ grant role partner#20032:EBess-4th.admin to role partner#20032:EBess-4th.owner by system and assume }",
"{ grant role person#EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
"{ grant role contact#4th.tenant to role partner#20032:EBess-4th.admin by system and assume }",
// agent
- "{ grant perm view on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.agent by system and assume }",
+ "{ grant perm SELECT on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.agent by system and assume }",
"{ grant role partner#20032:EBess-4th.agent to role partner#20032:EBess-4th.admin by system and assume }",
"{ grant role partner#20032:EBess-4th.agent to role person#EBess.admin by system and assume }",
"{ grant role partner#20032:EBess-4th.agent to role contact#4th.admin by system and assume }",
@@ -204,7 +204,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
"{ grant role contact#4th.guest to role partner#20032:EBess-4th.tenant by system and assume }",
// guest
- "{ grant perm view on partner#20032:EBess-4th to role partner#20032:EBess-4th.guest by system and assume }",
+ "{ grant perm SELECT on partner#20032:EBess-4th to role partner#20032:EBess-4th.guest by system and assume }",
"{ grant role partner#20032:EBess-4th.guest to role partner#20032:EBess-4th.tenant by system and assume }",
null)));
@@ -473,7 +473,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
- em.flush(); // TODO: why is that necessary?
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
index dd3e08c9..d3da9ada 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
@@ -113,11 +113,11 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
Array.from(
initialGrantNames,
"{ grant role hs_office_person#anothernewperson.owner to role global#global.admin by system and assume }",
- "{ grant perm edit on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
+ "{ grant perm UPDATE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
"{ grant role hs_office_person#anothernewperson.tenant to role hs_office_person#anothernewperson.admin by system and assume }",
- "{ grant perm * on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
+ "{ grant perm DELETE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
"{ grant role hs_office_person#anothernewperson.admin to role hs_office_person#anothernewperson.owner by system and assume }",
- "{ grant perm view on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.guest by system and assume }",
+ "{ grant perm SELECT on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.guest by system and assume }",
"{ grant role hs_office_person#anothernewperson.guest to role hs_office_person#anothernewperson.tenant by system and assume }",
"{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
));
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java
index 8d89479c..46d60a40 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java
@@ -115,14 +115,14 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm * on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
+ "{ grant perm DELETE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }",
- "{ grant perm edit on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
+ "{ grant perm UPDATE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
- "{ grant perm view on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
+ "{ grant perm SELECT on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
index 04b5b5cf..79910d28 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
@@ -131,11 +131,11 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
initialGrantNames,
// owner
- "{ grant perm * on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
+ "{ grant perm DELETE on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
"{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }",
// admin
- "{ grant perm edit on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
+ "{ grant perm UPDATE on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
"{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }",
"{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }",
@@ -151,7 +151,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
"{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
// guest
- "{ grant perm view on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }",
+ "{ grant perm SELECT on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }",
"{ grant role sepamandate#temprefB.guest to role sepamandate#temprefB.tenant by system and assume }",
null));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
index 9b6c14ed..968e5416 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
@@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.test.JpaAttempt;
@@ -43,7 +44,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
@Autowired
JpaAttempt jpaAttempt;
- private TreeMap> entitiesToCleanup = new TreeMap<>();
+ private TreeMap> entitiesToCleanup = new TreeMap<>();
private static Long latestIntialTestDataSerialId;
private static boolean countersInitialized = false;
@@ -61,7 +62,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return uuidToCleanup;
}
- public E toCleanup(final E entity) {
+ public E toCleanup(final E entity) {
out.println("toCleanup(" + entity.getClass() + ", " + entity.getUuid());
if ( entity.getUuid() == null ) {
throw new IllegalArgumentException("only persisted entities with valid uuid allowed");
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
index 6f0abc93..f56baf34 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
@@ -73,14 +73,16 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "global#global.admin"),
+ // TODO: should there be a grantedByRole or just a grantedByTrigger?
+ hasEntry("grantedByRoleIdName", "test_customer#xxx.owner"),
hasEntry("grantedRoleIdName", "test_customer#xxx.admin"),
hasEntry("granteeUserName", "customer-admin@xxx.example.com")
)
))
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "global#global.admin"),
+ // TODO: should there be a grantedByRole or just a grantedByTrigger?
+ hasEntry("grantedByRoleIdName", "test_customer#yyy.owner"),
hasEntry("grantedRoleIdName", "test_customer#yyy.admin"),
hasEntry("granteeUserName", "customer-admin@yyy.example.com")
)
@@ -296,7 +298,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
result.assertThat()
.statusCode(403)
.body("message", containsString("Access to granted role"))
- .body("message", containsString("forbidden for {test_package#xxx00.admin}"));
+ .body("message", containsString("forbidden for test_package#xxx00.admin"));
assertThat(findAllGrantsOf(givenCurrentUserAsPackageAdmin))
.extracting(RbacGrantEntity::getGranteeUserName)
.doesNotContain(givenNewUser.getName());
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
index 3b09e861..8ce615b7 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
@@ -84,7 +84,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseRbacGrantsAreReturned(
result,
- "{ grant role test_customer#xxx.admin to user customer-admin@xxx.example.com by role global#global.admin and assume }",
+ "{ grant role test_customer#xxx.admin to user customer-admin@xxx.example.com by role test_customer#xxx.owner and assume }",
"{ grant role test_package#xxx00.admin to user pac-admin-xxx00@xxx.example.com by role test_customer#xxx.admin and assume }",
"{ grant role test_package#xxx01.admin to user pac-admin-xxx01@xxx.example.com by role test_customer#xxx.admin and assume }",
"{ grant role test_package#xxx02.admin to user pac-admin-xxx02@xxx.example.com by role test_customer#xxx.admin and assume }");
@@ -162,8 +162,8 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
- "ERROR: [403] Access to granted role " + given.packageOwnerRoleUuid
- + " forbidden for {test_package#xxx00.admin}");
+ "ERROR: [403] Access to granted role test_package#xxx00.owner",
+ "forbidden for test_package#xxx00.admin");
jpaAttempt.transacted(() -> {
// finally, we use the new user to make sure, no roles were granted
context(given.arbitraryUser.getName(), null);
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
new file mode 100644
index 00000000..0e0421c8
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
@@ -0,0 +1,103 @@
+package net.hostsharing.hsadminng.rbac.rbacgrant;
+
+import net.hostsharing.hsadminng.context.Context;
+import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
+import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include;
+import net.hostsharing.test.JpaAttempt;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+
+import jakarta.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.UUID;
+
+import static java.lang.String.join;
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DataJpaTest
+@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class})
+class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanup {
+
+ @Autowired
+ RbacGrantsDiagramService grantsMermaidService;
+
+ @MockBean
+ HttpServletRequest request;
+
+ @Autowired
+ Context context;
+
+ @Autowired
+ RbacGrantsDiagramService diagramService;
+
+ TestInfo test;
+
+ @BeforeEach
+ void init(TestInfo testInfo) {
+ this.test = testInfo;
+ }
+
+ protected void context(final String currentUser, final String assumedRoles) {
+ context.define(test.getDisplayName(), null, currentUser, assumedRoles);
+ }
+
+ protected void context(final String currentUser) {
+ context(currentUser, null);
+ }
+
+ @Test
+ void allGrantsToCurrentUser() {
+ context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
+ final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES));
+
+ assertThat(graph).isEqualTo("""
+ flowchart TB
+
+ role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
+ role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
+ role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
+ role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
+ """.trim());
+ }
+
+ @Test
+ void allGrantsToCurrentUserIncludingPermissions() {
+ context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
+ final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES, Include.PERMISSIONS));
+
+ assertThat(graph).isEqualTo("""
+ flowchart TB
+
+ role:test_customer#xxx.tenant --> perm:SELECT:on:test_customer#xxx
+ role:test_domain#xxx00-aaaa.admin --> perm:SELECT:on:test_domain#xxx00-aaaa
+ role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
+ role:test_domain#xxx00-aaaa.owner --> perm:DELETE:on:test_domain#xxx00-aaaa
+ role:test_domain#xxx00-aaaa.owner --> perm:UPDATE:on:test_domain#xxx00-aaaa
+ role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
+ role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
+ role:test_package#xxx00.tenant --> perm:SELECT:on:test_package#xxx00
+ role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
+ """.trim());
+ }
+
+ @Test
+ @Disabled // enable to generate from a real database
+ void print() throws IOException {
+ //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
+ context("superuser-alex@hostsharing.net");
+
+ //final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS));
+
+ final var targetObject = (UUID) em.createNativeQuery("SELECT uuid FROM hs_office_coopassetstransaction WHERE reference='ref 1000101-1'").getSingleResult();
+ final var graph = grantsMermaidService.allGrantsFrom(targetObject, "view", EnumSet.of(Include.USERS));
+
+ RbacGrantsDiagramService.writeToFile(join(";", context.getAssumedRoles()), graph, "doc/all-grants.md");
+ }
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectEntity.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectEntity.java
new file mode 100644
index 00000000..d4256e56
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectEntity.java
@@ -0,0 +1,31 @@
+package net.hostsharing.hsadminng.rbac.rbacrole;
+
+import lombok.*;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.data.annotation.Immutable;
+
+import jakarta.persistence.*;
+import java.util.List;
+import java.util.UUID;
+
+@Entity
+@Table(name = "rbacobject") // TODO: create view rbacobject_ev
+@Getter
+@Setter
+@ToString
+@Immutable
+@NoArgsConstructor
+@AllArgsConstructor
+public class RawRbacObjectEntity {
+
+ @Id
+ private UUID uuid;
+
+ @Column(name="objecttable")
+ private String objectTable;
+
+ @NotNull
+ public static List objectDisplaysOf(@NotNull final List roles) {
+ return roles.stream().map(e -> e.objectTable+ "#" + e.uuid).sorted().toList();
+ }
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectRepository.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectRepository.java
new file mode 100644
index 00000000..ab645316
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacObjectRepository.java
@@ -0,0 +1,11 @@
+package net.hostsharing.hsadminng.rbac.rbacrole;
+
+import org.springframework.data.repository.Repository;
+
+import java.util.List;
+import java.util.UUID;
+
+public interface RawRbacObjectRepository extends Repository {
+
+ List findAll();
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
index b13bcb76..9d7e16ca 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
@@ -288,19 +288,15 @@ class RbacUserControllerAcceptanceTest {
.body("", hasItem(
allOf(
hasEntry("roleName", "test_customer#yyy.tenant"),
- hasEntry("op", "view"))
- ))
- .body("", hasItem(
- allOf(
- hasEntry("roleName", "test_package#yyy00.admin"),
- hasEntry("op", "add-domain"))
+ hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
- hasEntry("op", "*"))
+ hasEntry("op", "DELETE"))
))
- .body("size()", is(7));
+ // actual content tested in integration test, so this is enough for here:
+ .body("size()", greaterThanOrEqualTo(6));
// @formatter:on
}
@@ -313,7 +309,7 @@ class RbacUserControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_package#yyy00.admin")
+ .header("assumed-roles", "test_customer#yyy.admin")
.port(port)
.when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions")
@@ -323,19 +319,15 @@ class RbacUserControllerAcceptanceTest {
.body("", hasItem(
allOf(
hasEntry("roleName", "test_customer#yyy.tenant"),
- hasEntry("op", "view"))
- ))
- .body("", hasItem(
- allOf(
- hasEntry("roleName", "test_package#yyy00.admin"),
- hasEntry("op", "add-domain"))
+ hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
- hasEntry("op", "*"))
+ hasEntry("op", "DELETE"))
))
- .body("size()", is(7));
+ // actual content tested in integration test, so this is enough for here:
+ .body("size()", greaterThanOrEqualTo(6));
// @formatter:on
}
@@ -357,19 +349,15 @@ class RbacUserControllerAcceptanceTest {
.body("", hasItem(
allOf(
hasEntry("roleName", "test_customer#yyy.tenant"),
- hasEntry("op", "view"))
- ))
- .body("", hasItem(
- allOf(
- hasEntry("roleName", "test_package#yyy00.admin"),
- hasEntry("op", "add-domain"))
+ hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
- hasEntry("op", "*"))
+ hasEntry("op", "DELETE"))
))
- .body("size()", is(7));
+ // actual content tested in integration test, so this is enough for here:
+ .body("size()", greaterThanOrEqualTo(6));
// @formatter:on
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
index ea0a3109..c63047ed 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
@@ -20,6 +20,7 @@ import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.UUID;
+import static java.util.Comparator.comparing;
import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
@@ -181,50 +182,48 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
private static final String[] ALL_USER_PERMISSIONS = Array.of(
// @formatter:off
- "global#global.admin -> global#global: add-customer",
+ "test_customer#xxx.admin -> test_customer#xxx: SELECT",
+ "test_customer#xxx.owner -> test_customer#xxx: DELETE",
+ "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
+ "test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
+ "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01.tenant -> test_package#xxx01: SELECT",
+ "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02.tenant -> test_package#xxx02: SELECT",
- "test_customer#xxx.admin -> test_customer#xxx: add-package",
- "test_customer#xxx.admin -> test_customer#xxx: view",
- "test_customer#xxx.owner -> test_customer#xxx: *",
- "test_customer#xxx.tenant -> test_customer#xxx: view",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.tenant -> test_package#xxx00: view",
- "test_package#xxx01.admin -> test_package#xxx01: add-domain",
- "test_package#xxx01.admin -> test_package#xxx01: add-domain",
- "test_package#xxx01.tenant -> test_package#xxx01: view",
- "test_package#xxx02.admin -> test_package#xxx02: add-domain",
- "test_package#xxx02.admin -> test_package#xxx02: add-domain",
- "test_package#xxx02.tenant -> test_package#xxx02: view",
+ "test_customer#yyy.admin -> test_customer#yyy: SELECT",
+ "test_customer#yyy.owner -> test_customer#yyy: DELETE",
+ "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
+ "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
+ "test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
+ "test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
+ "test_package#yyy01.tenant -> test_package#yyy01: SELECT",
+ "test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
+ "test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
+ "test_package#yyy02.tenant -> test_package#yyy02: SELECT",
- "test_customer#yyy.admin -> test_customer#yyy: add-package",
- "test_customer#yyy.admin -> test_customer#yyy: view",
- "test_customer#yyy.owner -> test_customer#yyy: *",
- "test_customer#yyy.tenant -> test_customer#yyy: view",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.tenant -> test_package#yyy00: view",
- "test_package#yyy01.admin -> test_package#yyy01: add-domain",
- "test_package#yyy01.admin -> test_package#yyy01: add-domain",
- "test_package#yyy01.tenant -> test_package#yyy01: view",
- "test_package#yyy02.admin -> test_package#yyy02: add-domain",
- "test_package#yyy02.admin -> test_package#yyy02: add-domain",
- "test_package#yyy02.tenant -> test_package#yyy02: view",
-
- "test_customer#zzz.admin -> test_customer#zzz: add-package",
- "test_customer#zzz.admin -> test_customer#zzz: view",
- "test_customer#zzz.owner -> test_customer#zzz: *",
- "test_customer#zzz.tenant -> test_customer#zzz: view",
- "test_package#zzz00.admin -> test_package#zzz00: add-domain",
- "test_package#zzz00.admin -> test_package#zzz00: add-domain",
- "test_package#zzz00.tenant -> test_package#zzz00: view",
- "test_package#zzz01.admin -> test_package#zzz01: add-domain",
- "test_package#zzz01.admin -> test_package#zzz01: add-domain",
- "test_package#zzz01.tenant -> test_package#zzz01: view",
- "test_package#zzz02.admin -> test_package#zzz02: add-domain",
- "test_package#zzz02.admin -> test_package#zzz02: add-domain",
- "test_package#zzz02.tenant -> test_package#zzz02: view"
- // @formatter:on
+ "test_customer#zzz.admin -> test_customer#zzz: SELECT",
+ "test_customer#zzz.owner -> test_customer#zzz: DELETE",
+ "test_customer#zzz.tenant -> test_customer#zzz: SELECT",
+ "test_customer#zzz.admin -> test_customer#zzz: INSERT:test_package",
+ "test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
+ "test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
+ "test_package#zzz00.tenant -> test_package#zzz00: SELECT",
+ "test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
+ "test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
+ "test_package#zzz01.tenant -> test_package#zzz01: SELECT",
+ "test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
+ "test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
+ "test_package#zzz02.tenant -> test_package#zzz02: SELECT"
+ // @formatter:on
);
@Test
@@ -233,7 +232,9 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net");
// when
- final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-alex@hostsharing.net"));
+ final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-fran@hostsharing.net"))
+ .stream().filter(p -> p.getObjectTable().contains("test_"))
+ .sorted(comparing(RbacUserPermission::toString)).toList();
// then
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
@@ -251,32 +252,32 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.admin -> test_customer#xxx: add-package",
- "test_customer#xxx.admin -> test_customer#xxx: view",
- "test_customer#xxx.tenant -> test_customer#xxx: view",
+ "test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
+ "test_customer#xxx.admin -> test_customer#xxx: SELECT",
+ "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.tenant -> test_package#xxx00: view",
- "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: *",
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
+ "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
- "test_package#xxx01.admin -> test_package#xxx01: add-domain",
- "test_package#xxx01.admin -> test_package#xxx01: add-domain",
- "test_package#xxx01.tenant -> test_package#xxx01: view",
- "test_domain#xxx01-aaaa.owner -> test_domain#xxx01-aaaa: *",
+ "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01.tenant -> test_package#xxx01: SELECT",
+ "test_domain#xxx01-aaaa.owner -> test_domain#xxx01-aaaa: DELETE",
- "test_package#xxx02.admin -> test_package#xxx02: add-domain",
- "test_package#xxx02.admin -> test_package#xxx02: add-domain",
- "test_package#xxx02.tenant -> test_package#xxx02: view",
- "test_domain#xxx02-aaaa.owner -> test_domain#xxx02-aaaa: *"
+ "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02.tenant -> test_package#xxx02: SELECT",
+ "test_domain#xxx02-aaaa.owner -> test_domain#xxx02-aaaa: DELETE"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#yyy.admin -> test_customer#yyy: add-package",
- "test_customer#yyy.admin -> test_customer#yyy: view",
- "test_customer#yyy.tenant -> test_customer#yyy: view"
+ "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
+ "test_customer#yyy.admin -> test_customer#yyy: SELECT",
+ "test_customer#yyy.tenant -> test_customer#yyy: SELECT"
// @formatter:on
);
}
@@ -311,26 +312,26 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.tenant -> test_customer#xxx: view",
+ "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
// "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.tenant -> test_package#xxx00: view",
- "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: *",
- "test_domain#xxx00-aaab.owner -> test_domain#xxx00-aaab: *"
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
+ "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
+ "test_domain#xxx00-aaab.owner -> test_domain#xxx00-aaab: DELETE"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#yyy.admin -> test_customer#yyy: add-package",
- "test_customer#yyy.admin -> test_customer#yyy: view",
- "test_customer#yyy.tenant -> test_customer#yyy: view",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.tenant -> test_package#yyy00: view",
- "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: *",
- "test_domain#yyy00-aaab.owner -> test_domain#yyy00-aaab: *"
+ "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
+ "test_customer#yyy.admin -> test_customer#yyy: SELECT",
+ "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
+ "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
+ "test_domain#yyy00-aaab.owner -> test_domain#yyy00-aaab: DELETE"
// @formatter:on
);
}
@@ -359,11 +360,10 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.tenant -> test_customer#xxx: view",
+ "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
// "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.admin -> test_package#xxx00: add-domain",
- "test_package#xxx00.tenant -> test_package#xxx00: view"
+ "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00.tenant -> test_package#xxx00: SELECT"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
@@ -373,13 +373,13 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
"test_customer#xxx.admin -> test_customer#xxx: add-package",
// no permissions on other customer's objects
"test_customer#yyy.admin -> test_customer#yyy: add-package",
- "test_customer#yyy.admin -> test_customer#yyy: view",
- "test_customer#yyy.tenant -> test_customer#yyy: view",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.admin -> test_package#yyy00: add-domain",
- "test_package#yyy00.tenant -> test_package#yyy00: view",
- "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: *",
- "test_domain#yyy00-xxxb.owner -> test_domain#yyy00-xxxb: *"
+ "test_customer#yyy.admin -> test_customer#yyy: SELECT",
+ "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
+ "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
+ "test_domain#yyy00-xxxb.owner -> test_domain#yyy00-xxxb: DELETE"
// @formatter:on
);
}
@@ -432,7 +432,8 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
final List actualResult,
final String... expectedRoleNames) {
assertThat(actualResult)
- .extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp())
+ .extracting(p -> p.getRoleName() + " -> " + p.getObjectTable() + "#" + p.getObjectIdName() + ": " + p.getOp()
+ + (p.getOpTableName() != null ? (":"+p.getOpTableName()) : "" ))
.contains(expectedRoleNames);
}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
index 6c695caa..942351c0 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
@@ -148,7 +148,7 @@ class TestCustomerControllerAcceptanceTest {
// finally, the new customer can be viewed by its own admin
final var newUserUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
- context.define("customer-admin@uuu.example.com");
+ context.define("superuser-fran@hostsharing.net", "test_customer#uuu.admin");
assertThat(testCustomerRepository.findByUuid(newUserUuid))
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("uuu"));
}
@@ -175,7 +175,7 @@ class TestCustomerControllerAcceptanceTest {
.statusCode(403)
.contentType(ContentType.JSON)
.statusCode(403)
- .body("message", containsString("add-customer not permitted for test_customer#xxx.admin"));
+ .body("message", containsString("insert into test_customer not allowed for current subjects {test_customer#xxx.admin}"));
// @formatter:on
// finally, the new customer was not created
@@ -204,7 +204,7 @@ class TestCustomerControllerAcceptanceTest {
.statusCode(403)
.contentType(ContentType.JSON)
.statusCode(403)
- .body("message", containsString("add-customer not permitted for customer-admin@yyy.example.com"));
+ .body("message", containsString("insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
// @formatter:on
// finally, the new customer was not created
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
new file mode 100644
index 00000000..eca0aec1
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
@@ -0,0 +1,52 @@
+package net.hostsharing.hsadminng.test.cust;
+
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchartGenerator;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TestCustomerEntityUnitTest {
+
+ @Test
+ void definesRbac() {
+ final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(TestCustomerEntity.rbac()).toString();
+ assertThat(rbacFlowchart).isEqualTo("""
+ %%{init:{'flowchart':{'htmlLabels':false}}}%%
+ flowchart TB
+
+ subgraph customer["`**customer**`"]
+ direction TB
+ style customer fill:#dd4901,stroke:#274d6e,stroke-width:8px
+
+ subgraph customer:roles[ ]
+ style customer:roles fill:#dd4901,stroke:white
+
+ role:customer:owner[[customer:owner]]
+ role:customer:admin[[customer:admin]]
+ role:customer:tenant[[customer:tenant]]
+ end
+
+ subgraph customer:permissions[ ]
+ style customer:permissions fill:#dd4901,stroke:white
+
+ perm:customer:DELETE{{customer:DELETE}}
+ perm:customer:UPDATE{{customer:UPDATE}}
+ perm:customer:SELECT{{customer:SELECT}}
+ end
+ end
+
+ %% granting roles to users
+ user:creator ==>|XX| role:customer:owner
+
+ %% granting roles to roles
+ role:global:admin ==>|XX| role:customer:owner
+ role:customer:owner ==> role:customer:admin
+ role:customer:admin ==> role:customer:tenant
+
+ %% granting permissions to roles
+ role:customer:owner ==> perm:customer:DELETE
+ role:customer:admin ==> perm:customer:UPDATE
+ role:customer:tenant ==> perm:customer:SELECT
+ """);
+ }
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
index ca535142..27458b14 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
@@ -10,8 +10,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
-import jakarta.persistence.EntityManager;
-import jakarta.persistence.PersistenceContext;
import jakarta.persistence.PersistenceException;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
@@ -27,9 +25,6 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
@Autowired
TestCustomerRepository testCustomerRepository;
- @PersistenceContext
- EntityManager em;
-
@MockBean
HttpServletRequest request;
@@ -43,7 +38,6 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
final var count = testCustomerRepository.count();
// when
-
final var result = attempt(em, () -> {
final var newCustomer = new TestCustomerEntity(
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
@@ -72,7 +66,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// then
result.assertExceptionWithRootCauseMessage(
PersistenceException.class,
- "add-customer not permitted for test_customer#xxx.admin");
+ "ERROR: [403] insert into test_customer not allowed for current subjects {test_customer#xxx.admin}");
}
@Test
@@ -90,7 +84,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// then
result.assertExceptionWithRootCauseMessage(
PersistenceException.class,
- "add-customer not permitted for customer-admin@xxx.example.com");
+ "ERROR: [403] insert into test_customer not allowed for current subjects {customer-admin@xxx.example.com}");
}
@@ -116,15 +110,15 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
}
@Test
- public void globalAdmin_withAssumedglobalAdminRole_canViewAllCustomers() {
+ public void globalAdmin_withAssumedCustomerOwnerRole_canViewExactlyThatCustomer() {
given:
- context("superuser-alex@hostsharing.net", "global#global.admin");
+ context("superuser-alex@hostsharing.net", "test_customer#yyy.owner");
// when
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
then:
- allTheseCustomersAreReturned(result, "xxx", "yyy", "zzz");
+ allTheseCustomersAreReturned(result, "yyy");
}
@Test
@@ -141,6 +135,8 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnCustomer() {
+ context("customer-admin@xxx.example.com");
+
context("customer-admin@xxx.example.com", "test_package#xxx00.admin");
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
new file mode 100644
index 00000000..c5dccfd3
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
@@ -0,0 +1,68 @@
+package net.hostsharing.hsadminng.test.pac;
+
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacViewMermaidFlowchartGenerator;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TestPackageEntityUnitTest {
+
+ @Test
+ void definesRbac() {
+ final var rbacFlowchart = new RbacViewMermaidFlowchartGenerator(TestPackageEntity.rbac()).toString();
+ assertThat(rbacFlowchart).isEqualTo("""
+ %%{init:{'flowchart':{'htmlLabels':false}}}%%
+ flowchart TB
+
+ subgraph package["`**package**`"]
+ direction TB
+ style package fill:#dd4901,stroke:#274d6e,stroke-width:8px
+
+ subgraph package:roles[ ]
+ style package:roles fill:#dd4901,stroke:white
+
+ role:package:owner[[package:owner]]
+ role:package:admin[[package:admin]]
+ role:package:tenant[[package:tenant]]
+ end
+
+ subgraph package:permissions[ ]
+ style package:permissions fill:#dd4901,stroke:white
+
+ perm:package:INSERT{{package:INSERT}}
+ perm:package:DELETE{{package:DELETE}}
+ perm:package:UPDATE{{package:UPDATE}}
+ perm:package:SELECT{{package:SELECT}}
+ end
+ end
+
+ subgraph customer["`**customer**`"]
+ direction TB
+ style customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ subgraph customer:roles[ ]
+ style customer:roles fill:#99bcdb,stroke:white
+
+ role:customer:owner[[customer:owner]]
+ role:customer:admin[[customer:admin]]
+ role:customer:tenant[[customer:tenant]]
+ end
+ end
+
+ %% granting roles to roles
+ role:global:admin -.->|XX| role:customer:owner
+ role:customer:owner -.-> role:customer:admin
+ role:customer:admin -.-> role:customer:tenant
+ role:customer:admin ==> role:package:owner
+ role:package:owner ==> role:package:admin
+ role:package:admin ==> role:package:tenant
+ role:package:tenant ==> role:customer:tenant
+
+ %% granting permissions to roles
+ role:customer:admin ==> perm:package:INSERT
+ role:package:owner ==> perm:package:DELETE
+ role:package:owner ==> perm:package:UPDATE
+ role:package:tenant ==> perm:package:SELECT
+ """);
+ }
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
index 53d28e0c..a201d79e 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
@@ -1,6 +1,7 @@
package net.hostsharing.hsadminng.test.pac;
import net.hostsharing.hsadminng.context.Context;
+import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -19,10 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import( { Context.class, JpaAttempt.class })
-class TestPackageRepositoryIntegrationTest {
-
- @Autowired
- Context context;
+class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
@Autowired
TestPackageRepository testPackageRepository;
@@ -40,9 +38,10 @@ class TestPackageRepositoryIntegrationTest {
class FindAllByOptionalNameLike {
@Test
- public void globalAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
+ public void globalAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
// given
- context.define("superuser-alex@hostsharing.net");
+ // alex is not just global-admin but lso the creating user, thus we use fran
+ context.define("superuser-fran@hostsharing.net");
// when
final var result = testPackageRepository.findAllByOptionalNameLike(null);
@@ -52,7 +51,7 @@ class TestPackageRepositoryIntegrationTest {
}
@Test
- public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
+ public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
given:
context.define("superuser-alex@hostsharing.net", "global#global.admin");
@@ -89,7 +88,7 @@ class TestPackageRepositoryIntegrationTest {
class OptimisticLocking {
@Test
- public void supportsOptimisticLocking() throws InterruptedException {
+ public void supportsOptimisticLocking() {
// given
globalAdminWithAssumedRole("test_package#xxx00.admin");
final var pac = testPackageRepository.findAllByOptionalNameLike("%").get(0);
From 907e27ec1924362806df560999b582927a80b9d5 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 12 Mar 2024 10:13:36 +0100
Subject: [PATCH 02/12] import-debitor-relationship (into intermediate data
structure) (#22)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/22
Reviewed-by: Timotheus Pokorra
---
.../office/debitor/HsOfficeDebitorEntity.java | 6 ++--
.../HsOfficeRelationshipType.java | 2 +-
.../changelog/220-hs-office-relationship.sql | 2 +-
.../hs/office/migration/ImportOfficeData.java | 34 +++++++++++++++----
4 files changed, 32 insertions(+), 12 deletions(-)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 29a9452d..0f92c6af 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -116,7 +116,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
JOIN hs_office_relationship partnerRel
ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER'
JOIN hs_office_relationship debitorRel
- ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'ACCOUNTING'
+ ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'DEBITOR'
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00')
from hs_office_debitor as debitor
@@ -137,7 +137,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
fetchedBySql("""
SELECT *
FROM hs_office_relationship AS r
- WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid
"""),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
@@ -148,7 +148,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
SELECT *
FROM hs_office_relationship AS r
- WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid
""")
)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java
index 2b9fe60c..57053b1c 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java
@@ -6,7 +6,7 @@ public enum HsOfficeRelationshipType {
EX_PARTNER,
REPRESENTATIVE,
VIP_CONTACT,
- ACCOUNTING,
+ DEBITOR,
OPERATIONS,
SUBSCRIBER
}
diff --git a/src/main/resources/db/changelog/220-hs-office-relationship.sql b/src/main/resources/db/changelog/220-hs-office-relationship.sql
index 44f9e500..a2abece1 100644
--- a/src/main/resources/db/changelog/220-hs-office-relationship.sql
+++ b/src/main/resources/db/changelog/220-hs-office-relationship.sql
@@ -9,8 +9,8 @@ CREATE TYPE HsOfficeRelationshipType AS ENUM (
'PARTNER',
'EX_PARTNER',
'REPRESENTATIVE',
+ 'DEBITOR',
'VIP_CONTACT',
- 'ACCOUNTING',
'OPERATIONS',
'SUBSCRIBER');
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index 929aa919..0c86dc66 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -175,7 +175,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(1011)
+ @Order(1019)
void verifyBusinessPartners() {
assumeThatWeAreImportingControlledTestData();
@@ -220,6 +220,23 @@ public class ImportOfficeData extends ContextBasedTest {
@Test
@Order(1021)
+ void buildDebitorRelationships() {
+ debitors.forEach( (id, debitor) -> {
+ final var debitorRel = HsOfficeRelationshipEntity.builder()
+ .relType(HsOfficeRelationshipType.DEBITOR)
+ .relAnchor(debitor.getPartner().getPartnerRole().getRelHolder())
+ .relHolder(debitor.getPartner().getPartnerRole().getRelHolder()) // just 1 debitor/partner in legacy hsadmin
+ .contact(debitor.getBillingContact())
+ .build();
+ if (debitorRel.getRelAnchor() != null && debitorRel.getRelHolder() != null &&
+ debitorRel.getContact() != null ) {
+ relationships.put(relationshipId++, debitorRel);
+ }
+ });
+ }
+
+ @Test
+ @Order(1029)
void verifyContacts() {
assumeThatWeAreImportingControlledTestData();
@@ -289,8 +306,11 @@ public class ImportOfficeData extends ContextBasedTest {
2000013=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
2000014=rel(relAnchor='?? Test PS', relType='OPERATIONS', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000015=rel(relAnchor='?? Test PS', relType='REPRESENTATIVE', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000016=rel(relAnchor='NP Mellies, Michael', relType='SUBSCRIBER', relMark='operations-announce', relHolder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga ')
- }
+ 2000016=rel(relAnchor='NP Mellies, Michael', relType='SUBSCRIBER', relMark='operations-announce', relHolder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
+ 2000017=rel(relAnchor='NP Mellies, Michael', relType='DEBITOR', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000018=rel(relAnchor='LP JM GmbH', relType='DEBITOR', relHolder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
+ 2000019=rel(relAnchor='?? Test PS', relType='DEBITOR', relHolder='?? Test PS', contact='Petra Schmidt , Test PS')
+ }
""");
}
@@ -307,7 +327,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(1031)
+ @Order(1039)
void verifySepaMandates() {
assumeThatWeAreImportingControlledTestData();
@@ -339,7 +359,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(1041)
+ @Order(1049)
void verifyCoopShares() {
assumeThatWeAreImportingControlledTestData();
@@ -366,7 +386,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(1051)
+ @Order(1059)
void verifyCoopAssets() {
assumeThatWeAreImportingControlledTestData();
@@ -398,7 +418,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(2001)
+ @Order(2009)
void removeEmptyRelationships() {
assumeThatWeAreImportingControlledTestData();
From 3faf2ea99e725841610c389cb7d6d251c132e6cf Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Wed, 13 Mar 2024 15:01:24 +0100
Subject: [PATCH 03/12] rename partnerRole -> partnerRel, relationship ->
relation and remove rel-Prefix (relAnchor etc.) (#23)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/23
Reviewed-by: Timotheus Pokorra
---
doc/hs-office-data-structure.md | 85 +++---
.../office/debitor/HsOfficeDebitorEntity.java | 26 +-
.../partner/HsOfficePartnerController.java | 24 +-
.../partner/HsOfficePartnerDetailsEntity.java | 10 +-
.../office/partner/HsOfficePartnerEntity.java | 16 +-
.../HsOfficeRelationController.java} | 80 +++---
.../HsOfficeRelationEntity.java} | 62 ++---
.../HsOfficeRelationEntityPatcher.java} | 12 +-
.../relation/HsOfficeRelationRepository.java | 37 +++
.../HsOfficeRelationType.java} | 4 +-
.../HsOfficeRelationshipRepository.java | 37 ---
.../HsOfficeSepaMandateEntity.java | 4 +-
.../hsadminng/rbac/rbacdef/RbacView.java | 4 +-
.../hs-office/api-mappings.yaml | 2 +-
.../hs-office/hs-office-partner-schemas.yaml | 14 +-
....yaml => hs-office-relations-schemas.yaml} | 34 +--
...aml => hs-office-relations-with-uuid.yaml} | 34 +--
...ionships.yaml => hs-office-relations.yaml} | 28 +-
.../api-definition/hs-office/hs-office.yaml | 10 +-
.../db/changelog/220-hs-office-relation.sql | 36 +++
.../changelog/220-hs-office-relationship.sql | 36 ---
...rbac.md => 223-hs-office-relation-rbac.md} | 14 +-
...ac.sql => 223-hs-office-relation-rbac.sql} | 112 ++++----
...l => 228-hs-office-relation-test-data.sql} | 44 +--
.../db/changelog/230-hs-office-partner.sql | 6 +-
.../changelog/233-hs-office-partner-rbac.sql | 30 +--
.../238-hs-office-partner-test-data.sql | 20 +-
.../db/changelog/db.changelog-master.yaml | 6 +-
.../hsadminng/arch/ArchitectureTest.java | 12 +-
.../hs/office/migration/ImportOfficeData.java | 130 ++++-----
...OfficePartnerControllerAcceptanceTest.java | 44 +--
.../HsOfficePartnerControllerRestTest.java | 38 +--
...fficePartnerRepositoryIntegrationTest.java | 92 +++----
...ficeRelationControllerAcceptanceTest.java} | 252 +++++++++---------
...sOfficeRelationEntityPatcherUnitTest.java} | 36 +--
.../HsOfficeRelationEntityUnitTest.java | 43 +++
...iceRelationRepositoryIntegrationTest.java} | 228 ++++++++--------
.../HsOfficeRelationshipEntityUnitTest.java | 44 ---
tools/generate | 41 ---
39 files changed, 873 insertions(+), 914 deletions(-)
rename src/main/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipController.java => relation/HsOfficeRelationController.java} (51%)
rename src/main/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipEntity.java => relation/HsOfficeRelationEntity.java} (66%)
rename src/main/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipEntityPatcher.java => relation/HsOfficeRelationEntityPatcher.java} (67%)
create mode 100644 src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepository.java
rename src/main/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipType.java => relation/HsOfficeRelationType.java} (56%)
delete mode 100644 src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java
rename src/main/resources/api-definition/hs-office/{hs-office-relationship-schemas.yaml => hs-office-relations-schemas.yaml} (73%)
rename src/main/resources/api-definition/hs-office/{hs-office-relationships-with-uuid.yaml => hs-office-relations-with-uuid.yaml} (64%)
rename src/main/resources/api-definition/hs-office/{hs-office-relationships.yaml => hs-office-relations.yaml} (61%)
create mode 100644 src/main/resources/db/changelog/220-hs-office-relation.sql
delete mode 100644 src/main/resources/db/changelog/220-hs-office-relationship.sql
rename src/main/resources/db/changelog/{223-hs-office-relationship-rbac.md => 223-hs-office-relation-rbac.md} (64%)
rename src/main/resources/db/changelog/{223-hs-office-relationship-rbac.sql => 223-hs-office-relation-rbac.sql} (54%)
rename src/main/resources/db/changelog/{228-hs-office-relationship-test-data.sql => 228-hs-office-relation-test-data.sql} (57%)
rename src/test/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipControllerAcceptanceTest.java => relation/HsOfficeRelationControllerAcceptanceTest.java} (63%)
rename src/test/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipEntityPatcherUnitTest.java => relation/HsOfficeRelationEntityPatcherUnitTest.java} (67%)
create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityUnitTest.java
rename src/test/java/net/hostsharing/hsadminng/hs/office/{relationship/HsOfficeRelationshipRepositoryIntegrationTest.java => relation/HsOfficeRelationRepositoryIntegrationTest.java} (54%)
delete mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java
delete mode 100755 tools/generate
diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md
index 960e572b..b84264d0 100644
--- a/doc/hs-office-data-structure.md
+++ b/doc/hs-office-data-structure.md
@@ -10,7 +10,7 @@ classDiagram
namespace Partner {
class partner-MeierGmbH
- class role-MeierGmbH
+ class rel-MeierGmbH
class personDetails-MeierGmbH
class contactData-MeierGmbH
class person-MeierGmbH
@@ -19,28 +19,29 @@ classDiagram
namespace Representatives {
class person-FrankMeier
class contactData-FrankMeier
- class role-MeierGmbH-FrankMeier
+ class rel-MeierGmbH-FrankMeier
}
namespace Debitors {
class debitor-MeierGmbH
class contactData-MeierGmbH-Buha
- class role-MeierGmbH-Buha
+ class rel-MeierGmbH-Buha
}
namespace Operations {
class person-SabineMeier
class contactData-SabineMeier
- class role-MeierGmbH-SabineMeier
+ class rel-MeierGmbH-SabineMeier
}
namespace Enums {
- class RoleType {
+ class RelationType {
<>
UNKNOWN
+ PARTNER
+ DEBITOR
REPRESENTATIVE
- ACCOUNTING
OPERATIONS
}
@@ -64,9 +65,9 @@ classDiagram
class partner-MeierGmbH {
+Numeric partnerNumber: 12345
- +Role partnerRole
+ +Relation partnerRel
}
- partner-MeierGmbH *-- role-MeierGmbH
+ partner-MeierGmbH *-- rel-MeierGmbH
class person-MeierGmbH {
+personType: LEGAL
@@ -90,32 +91,32 @@ classDiagram
+emailAddresses: office@meier-gmbh.de
}
- class role-MeierGmbH {
- +RoleType RoleType PARTNER
+ class rel-MeierGmbH {
+ +RelationType type PARTNER
+Person anchor
+Person holder
- +Contact roleContact
+ +Contact contact
}
- role-MeierGmbH o-- person-HostsharingEG : anchor
- role-MeierGmbH o-- person-MeierGmbH : holder
- role-MeierGmbH o-- contactData-MeierGmbH
+ rel-MeierGmbH o-- person-HostsharingEG : anchor
+ rel-MeierGmbH o-- person-MeierGmbH : holder
+ rel-MeierGmbH o-- contactData-MeierGmbH
%% --- Debitors ---
class debitor-MeierGmbH {
- +Partner partner
- +Numeric[2] debitorNumberSuffix: 00
- +Role billingRole
- +boolean billable: true
- +String vatId: ID123456789
- +String vatCountryCode: DE
- +boolean vatBusiness: true
- +boolean vatReverseCharge: false
+ +Partner partner
+ +Numeric[2] debitorNumberSuffix: 00
+ +Relation debitorRel
+ +boolean billable: true
+ +String vatId: ID123456789
+ +String vatCountryCode: DE
+ +boolean vatBusiness: true
+ +boolean vatReverseCharge: false
+BankAccount refundBankAccount
- +String defaultPrefix: mei
+ +String defaultPrefix: mei
}
debitor-MeierGmbH o-- partner-MeierGmbH
- debitor-MeierGmbH *-- role-MeierGmbH-Buha
+ debitor-MeierGmbH *-- rel-MeierGmbH-Buha
class contactData-MeierGmbH-Buha {
+postalAddress: Hauptstraße 5, 22345 Hamburg
@@ -123,15 +124,15 @@ classDiagram
+emailAddresses: buha@meier-gmbh.de
}
- class role-MeierGmbH-Buha {
- +RoleType RoleType ACCOUNTING
+ class rel-MeierGmbH-Buha {
+ +RelationType type DEBITOR
+Person anchor
+Person holder
- +Contact roleContact
+ +Contact contact
}
- role-MeierGmbH-Buha o-- person-MeierGmbH : anchor
- role-MeierGmbH-Buha o-- person-MeierGmbH : holder
- role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha
+ rel-MeierGmbH-Buha o-- person-MeierGmbH : anchor
+ rel-MeierGmbH-Buha o-- person-MeierGmbH : holder
+ rel-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha
%% --- Representatives ---
@@ -148,15 +149,15 @@ classDiagram
+emailAddresses: frank.meier@meier-gmbh.de
}
- class role-MeierGmbH-FrankMeier {
- +RoleType RoleType REPRESENTATIVE
+ class rel-MeierGmbH-FrankMeier {
+ +RelationType type REPRESENTATIVE
+Person anchor
+Person holder
- +Contact roleContact
+ +Contact contact
}
- role-MeierGmbH-FrankMeier o-- person-MeierGmbH : anchor
- role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder
- role-MeierGmbH-FrankMeier o-- contactData-FrankMeier
+ rel-MeierGmbH-FrankMeier o-- person-MeierGmbH : anchor
+ rel-MeierGmbH-FrankMeier o-- person-FrankMeier : holder
+ rel-MeierGmbH-FrankMeier o-- contactData-FrankMeier
%% --- Operations ---
@@ -173,14 +174,14 @@ classDiagram
+emailAddresses: sabine.meier@meier-gmbh.de
}
- class role-MeierGmbH-SabineMeier {
- +RoleType RoleType OPERATIONAL
+ class rel-MeierGmbH-SabineMeier {
+ +RelationType type OPERATIONAL
+Person anchor
+Person holder
- +Contact roleContact
+ +Contact contact
}
- role-MeierGmbH-SabineMeier o-- person-MeierGmbH : anchor
- role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder
- role-MeierGmbH-SabineMeier o-- contactData-SabineMeier
+ rel-MeierGmbH-SabineMeier o-- person-MeierGmbH : anchor
+ rel-MeierGmbH-SabineMeier o-- person-SabineMeier : holder
+ rel-MeierGmbH-SabineMeier o-- contactData-SabineMeier
```
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 0f92c6af..66f82f95 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@@ -113,10 +113,10 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
SELECT debitor.uuid,
'D-' || (SELECT partner.partnerNumber
FROM hs_office_partner partner
- JOIN hs_office_relationship partnerRel
- ON partnerRel.uuid = partner.partnerRoleUUid AND partnerRel.relType = 'PARTNER'
- JOIN hs_office_relationship debitorRel
- ON debitorRel.relAnchorUuid = partnerRel.relHolderUuid AND partnerRel.relType = 'DEBITOR'
+ 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 partnerRel.type = 'DEBITOR'
WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| to_char(debitorNumberSuffix, 'fm00')
from hs_office_debitor as debitor
@@ -133,11 +133,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"defaultPrefix" /* TODO: do we want that updatable? */)
.createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
- .importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
+ .importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
fetchedBySql("""
SELECT *
- FROM hs_office_relationship AS r
- WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ FROM hs_office_relation AS r
+ WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
"""),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
@@ -147,18 +147,18 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
SELECT *
- FROM hs_office_relationship AS r
- WHERE r.relType = 'DEBITOR' AND r.relHolderUuid = ${REF}.debitorRelUuid
+ FROM hs_office_relation AS r
+ WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
""")
)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
- .importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
+ .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("partnerRelUuid"), fetchedBySql("""
SELECT *
- FROM hs_office_relationship AS partnerRel
- WHERE ${debitorRel}.relAnchorUuid = partnerRel.relHolderUuid
+ FROM hs_office_relation AS partnerRel
+ WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
""")
)
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
index 6fdd0732..7a3813b9 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerController.java
@@ -7,11 +7,11 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartners
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
-import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRoleInsertResource;
+import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerRelInsertResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import org.springframework.beans.factory.annotation.Autowired;
@@ -40,7 +40,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerRepository partnerRepo;
@Autowired
- private HsOfficeRelationshipRepository relationshipRepo;
+ private HsOfficeRelationRepository relationRepo;
@PersistenceContext
private EntityManager em;
@@ -112,7 +112,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
if (partnerRepo.deleteByUuid(partnerUuid) != 1 ||
// TODO: move to after delete trigger in partner
- relationshipRepo.deleteByUuid(partnerToDelete.get().getPartnerRole().getUuid()) != 1 ) {
+ relationRepo.deleteByUuid(partnerToDelete.get().getPartnerRel().getUuid()) != 1 ) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
@@ -141,18 +141,18 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
final var entityToSave = new HsOfficePartnerEntity();
entityToSave.setPartnerNumber(body.getPartnerNumber());
- entityToSave.setPartnerRole(persistPartnerRole(body.getPartnerRole()));
+ entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
entityToSave.setContact(ref(HsOfficeContactEntity.class, body.getContactUuid()));
entityToSave.setPerson(ref(HsOfficePersonEntity.class, body.getPersonUuid()));
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
return entityToSave;
}
- private HsOfficeRelationshipEntity persistPartnerRole(final HsOfficePartnerRoleInsertResource resource) {
- final var entity = new HsOfficeRelationshipEntity();
- entity.setRelType(HsOfficeRelationshipType.PARTNER);
- entity.setRelAnchor(ref(HsOfficePersonEntity.class, resource.getRelAnchorUuid()));
- entity.setRelHolder(ref(HsOfficePersonEntity.class, resource.getRelHolderUuid()));
+ private HsOfficeRelationEntity persistPartnerRel(final HsOfficePartnerRelInsertResource resource) {
+ final var entity = new HsOfficeRelationEntity();
+ entity.setType(HsOfficeRelationType.PARTNER);
+ entity.setAnchor(ref(HsOfficePersonEntity.class, resource.getAnchorUuid()));
+ entity.setHolder(ref(HsOfficePersonEntity.class, resource.getHolderUuid()));
entity.setContact(ref(HsOfficeContactEntity.class, resource.getContactUuid()));
em.persist(entity);
return entity;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index e557f9ae..435357fe 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@@ -86,15 +86,15 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
"dateOfDeath")
.createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
- .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
+ .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
fetchedBySql("""
SELECT partnerRel.*
- FROM hs_office_relationship AS partnerRel
+ FROM hs_office_relation AS partnerRel
JOIN hs_office_partner AS partner
ON partner.detailsUuid = ${ref}.uuid
- WHERE partnerRel.uuid = partner.partnerRoleUuid
+ WHERE partnerRel.uuid = partner.partnerRelUuid
"""),
- dependsOnColumn("partnerRoleUuid"))
+ dependsOnColumn("partnerRelUuid"))
// The grants are defined in HsOfficePartnerEntity.rbac()
// because they have to be changed when its partnerRel changes,
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
index aa000f67..8e35e9b0 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
@@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -50,15 +50,15 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private Integer partnerNumber;
@ManyToOne
- @JoinColumn(name = "partnerroleuuid", nullable = false)
- private HsOfficeRelationshipEntity partnerRole;
+ @JoinColumn(name = "partnerreluuid", nullable = false)
+ private HsOfficeRelationEntity partnerRel;
- // TODO: remove, is replaced by partnerRole
+ // TODO: remove, is replaced by partnerRel
@ManyToOne
@JoinColumn(name = "personuuid", nullable = false)
private HsOfficePersonEntity person;
- // TODO: remove, is replaced by partnerRole
+ // TODO: remove, is replaced by partnerRel
@ManyToOne
@JoinColumn(name = "contactuuid", nullable = false)
private HsOfficeContactEntity contact;
@@ -87,13 +87,13 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
FROM hs_office_partner AS partner
"""))
.withUpdatableColumns(
- "partnerRoleUuid",
+ "partnerRelUuid",
"personUuid",
"contactUuid")
.createPermission(custom("new-partner")).grantedTo("global", ADMIN)
- .importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
- fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
+ .importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
+ fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
similarity index 51%
rename from src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java
rename to src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
index 98c6bccf..a7923128 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
@@ -1,8 +1,8 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
-import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationshipsApi;
+import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeRelationsApi;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.*;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
@@ -22,7 +22,7 @@ import java.util.function.BiConsumer;
@RestController
-public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi {
+public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Autowired
private Context context;
@@ -31,10 +31,10 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
private Mapper mapper;
@Autowired
- private HsOfficeRelationshipRepository relationshipRepo;
+ private HsOfficeRelationRepository relationRepo;
@Autowired
- private HsOfficePersonRepository relHolderRepo;
+ private HsOfficePersonRepository holderRepo;
@Autowired
private HsOfficeContactRepository contactRepo;
@@ -44,79 +44,79 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
@Override
@Transactional(readOnly = true)
- public ResponseEntity> listRelationships(
+ public ResponseEntity> listRelations(
final String currentUser,
final String assumedRoles,
final UUID personUuid,
- final HsOfficeRelationshipTypeResource relationshipType) {
+ final HsOfficeRelationTypeResource relationType) {
context.define(currentUser, assumedRoles);
- final var entities = relationshipRepo.findRelationshipRelatedToPersonUuidAndRelationshipType(personUuid,
- mapper.map(relationshipType, HsOfficeRelationshipType.class));
+ final var entities = relationRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
+ mapper.map(relationType, HsOfficeRelationType.class));
- final var resources = mapper.mapList(entities, HsOfficeRelationshipResource.class,
- RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER);
+ final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
+ RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.ok(resources);
}
@Override
@Transactional
- public ResponseEntity addRelationship(
+ public ResponseEntity addRelation(
final String currentUser,
final String assumedRoles,
- final HsOfficeRelationshipInsertResource body) {
+ final HsOfficeRelationInsertResource body) {
context.define(currentUser, assumedRoles);
- final var entityToSave = new HsOfficeRelationshipEntity();
- entityToSave.setRelType(HsOfficeRelationshipType.valueOf(body.getRelType()));
- entityToSave.setRelAnchor(relHolderRepo.findByUuid(body.getRelAnchorUuid()).orElseThrow(
- () -> new NoSuchElementException("cannot find relAnchorUuid " + body.getRelAnchorUuid())
+ final var entityToSave = new HsOfficeRelationEntity();
+ entityToSave.setType(HsOfficeRelationType.valueOf(body.getType()));
+ entityToSave.setAnchor(holderRepo.findByUuid(body.getAnchorUuid()).orElseThrow(
+ () -> new NoSuchElementException("cannot find anchorUuid " + body.getAnchorUuid())
));
- entityToSave.setRelHolder(relHolderRepo.findByUuid(body.getRelHolderUuid()).orElseThrow(
- () -> new NoSuchElementException("cannot find relHolderUuid " + body.getRelHolderUuid())
+ entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow(
+ () -> new NoSuchElementException("cannot find holderUuid " + body.getHolderUuid())
));
entityToSave.setContact(contactRepo.findByUuid(body.getContactUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find contactUuid " + body.getContactUuid())
));
- final var saved = relationshipRepo.save(entityToSave);
+ final var saved = relationRepo.save(entityToSave);
final var uri =
MvcUriComponentsBuilder.fromController(getClass())
- .path("/api/hs/office/relationships/{id}")
+ .path("/api/hs/office/relations/{id}")
.buildAndExpand(saved.getUuid())
.toUri();
- final var mapped = mapper.map(saved, HsOfficeRelationshipResource.class,
- RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER);
+ final var mapped = mapper.map(saved, HsOfficeRelationResource.class,
+ RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
return ResponseEntity.created(uri).body(mapped);
}
@Override
@Transactional(readOnly = true)
- public ResponseEntity getRelationshipByUuid(
+ public ResponseEntity getRelationByUuid(
final String currentUser,
final String assumedRoles,
- final UUID relationshipUuid) {
+ final UUID relationUuid) {
context.define(currentUser, assumedRoles);
- final var result = relationshipRepo.findByUuid(relationshipUuid);
+ final var result = relationRepo.findByUuid(relationUuid);
if (result.isEmpty()) {
return ResponseEntity.notFound().build();
}
- return ResponseEntity.ok(mapper.map(result.get(), HsOfficeRelationshipResource.class, RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER));
+ return ResponseEntity.ok(mapper.map(result.get(), HsOfficeRelationResource.class, RELATION_ENTITY_TO_RESOURCE_POSTMAPPER));
}
@Override
@Transactional
- public ResponseEntity deleteRelationshipByUuid(
+ public ResponseEntity deleteRelationByUuid(
final String currentUser,
final String assumedRoles,
- final UUID relationshipUuid) {
+ final UUID relationUuid) {
context.define(currentUser, assumedRoles);
- final var result = relationshipRepo.deleteByUuid(relationshipUuid);
+ final var result = relationRepo.deleteByUuid(relationUuid);
if (result == 0) {
return ResponseEntity.notFound().build();
}
@@ -126,27 +126,27 @@ public class HsOfficeRelationshipController implements HsOfficeRelationshipsApi
@Override
@Transactional
- public ResponseEntity patchRelationship(
+ public ResponseEntity patchRelation(
final String currentUser,
final String assumedRoles,
- final UUID relationshipUuid,
- final HsOfficeRelationshipPatchResource body) {
+ final UUID relationUuid,
+ final HsOfficeRelationPatchResource body) {
context.define(currentUser, assumedRoles);
- final var current = relationshipRepo.findByUuid(relationshipUuid).orElseThrow();
+ final var current = relationRepo.findByUuid(relationUuid).orElseThrow();
- new HsOfficeRelationshipEntityPatcher(em, current).apply(body);
+ new HsOfficeRelationEntityPatcher(em, current).apply(body);
- final var saved = relationshipRepo.save(current);
- final var mapped = mapper.map(saved, HsOfficeRelationshipResource.class);
+ final var saved = relationRepo.save(current);
+ final var mapped = mapper.map(saved, HsOfficeRelationResource.class);
return ResponseEntity.ok(mapped);
}
- final BiConsumer RELATIONSHIP_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
- resource.setRelAnchor(mapper.map(entity.getRelAnchor(), HsOfficePersonResource.class));
- resource.setRelHolder(mapper.map(entity.getRelHolder(), HsOfficePersonResource.class));
+ final BiConsumer RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
+ resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class));
+ resource.setHolder(mapper.map(entity.getHolder(), HsOfficePersonResource.class));
resource.setContact(mapper.map(entity.getContact(), HsOfficeContactResource.class));
};
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
similarity index 66%
rename from src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java
rename to src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
index 1ec9fd74..71e2b11a 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
@@ -1,4 +1,4 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import lombok.*;
import lombok.experimental.FieldNameConstants;
@@ -24,49 +24,49 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
-@Table(name = "hs_office_relationship_rv")
+@Table(name = "hs_office_relation_rv")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
-public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
+public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
- private static Stringify toString = stringify(HsOfficeRelationshipEntity.class, "rel")
- .withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor)
- .withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType)
- .withProp(Fields.relMark, HsOfficeRelationshipEntity::getRelMark)
- .withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder)
- .withProp(Fields.contact, HsOfficeRelationshipEntity::getContact);
+ private static Stringify toString = stringify(HsOfficeRelationEntity.class, "rel")
+ .withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
+ .withProp(Fields.type, HsOfficeRelationEntity::getType)
+ .withProp(Fields.mark, HsOfficeRelationEntity::getMark)
+ .withProp(Fields.holder, HsOfficeRelationEntity::getHolder)
+ .withProp(Fields.contact, HsOfficeRelationEntity::getContact);
- private static Stringify toShortString = stringify(HsOfficeRelationshipEntity.class, "rel")
- .withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor)
- .withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType)
- .withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder);
+ private static Stringify toShortString = stringify(HsOfficeRelationEntity.class, "rel")
+ .withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
+ .withProp(Fields.type, HsOfficeRelationEntity::getType)
+ .withProp(Fields.holder, HsOfficeRelationEntity::getHolder);
@Id
@GeneratedValue
private UUID uuid;
@ManyToOne
- @JoinColumn(name = "relanchoruuid")
- private HsOfficePersonEntity relAnchor;
+ @JoinColumn(name = "anchoruuid")
+ private HsOfficePersonEntity anchor;
@ManyToOne
- @JoinColumn(name = "relholderuuid")
- private HsOfficePersonEntity relHolder;
+ @JoinColumn(name = "holderuuid")
+ private HsOfficePersonEntity holder;
@ManyToOne
@JoinColumn(name = "contactuuid")
private HsOfficeContactEntity contact;
- @Column(name = "reltype")
+ @Column(name = "type")
@Enumerated(EnumType.STRING)
- private HsOfficeRelationshipType relType;
+ private HsOfficeRelationType type;
- @Column(name = "relmark")
- private String relMark;
+ @Column(name = "mark")
+ private String mark;
@Override
public String toString() {
@@ -79,22 +79,22 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
}
public static RbacView rbac() {
- return rbacViewFor("relationship", HsOfficeRelationshipEntity.class)
+ return rbacViewFor("relation", HsOfficeRelationEntity.class)
.withIdentityView(SQL.projection("""
- (select idName from hs_office_person_iv p where p.uuid = relAnchorUuid)
- || '-with-' || target.relType || '-'
- || (select idName from hs_office_person_iv p where p.uuid = relHolderUuid)
+ (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)
"""))
.withRestrictedViewOrderBy(SQL.expression(
- "(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)"))
+ "(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
- dependsOnColumn("relAnchorUuid"),
- fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relAnchorUuid")
+ dependsOnColumn("anchorUuid"),
+ fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
- dependsOnColumn("relHolderUuid"),
- fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.relHolderUuid")
+ dependsOnColumn("holderUuid"),
+ fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
)
.importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"),
@@ -123,6 +123,6 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("223-hs-office-relationship-rbac-generated");
+ rbac().generateWithBaseFileName("223-hs-office-relation-rbac-generated");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcher.java
similarity index 67%
rename from src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcher.java
rename to src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcher.java
index fa080ba2..aeaae5ea 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcher.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcher.java
@@ -1,25 +1,25 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
-import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipPatchResource;
+import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import jakarta.persistence.EntityManager;
import java.util.UUID;
-class HsOfficeRelationshipEntityPatcher implements EntityPatcher {
+class HsOfficeRelationEntityPatcher implements EntityPatcher {
private final EntityManager em;
- private final HsOfficeRelationshipEntity entity;
+ private final HsOfficeRelationEntity entity;
- HsOfficeRelationshipEntityPatcher(final EntityManager em, final HsOfficeRelationshipEntity entity) {
+ HsOfficeRelationEntityPatcher(final EntityManager em, final HsOfficeRelationEntity entity) {
this.em = em;
this.entity = entity;
}
@Override
- public void apply(final HsOfficeRelationshipPatchResource resource) {
+ public void apply(final HsOfficeRelationPatchResource resource) {
OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "contact");
entity.setContact(em.getReference(HsOfficeContactEntity.class, newValue));
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepository.java
new file mode 100644
index 00000000..95bac3a2
--- /dev/null
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepository.java
@@ -0,0 +1,37 @@
+package net.hostsharing.hsadminng.hs.office.relation;
+
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.Repository;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+public interface HsOfficeRelationRepository extends Repository {
+
+ Optional findByUuid(UUID id);
+
+ default List findRelationRelatedToPersonUuidAndRelationType(@NotNull UUID personUuid, HsOfficeRelationType relationType) {
+ return findRelationRelatedToPersonUuidAndRelationTypeString(personUuid, relationType.toString());
+ }
+
+ @Query(value = """
+ SELECT p.* FROM hs_office_relation_rv AS p
+ WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
+ """, nativeQuery = true)
+ List findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
+
+ @Query(value = """
+ SELECT p.* FROM hs_office_relation_rv AS p
+ WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
+ AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
+ """, nativeQuery = true)
+ List findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
+
+ HsOfficeRelationEntity save(final HsOfficeRelationEntity entity);
+
+ long count();
+
+ int deleteByUuid(UUID uuid);
+}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationType.java
similarity index 56%
rename from src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java
rename to src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationType.java
index 57053b1c..035c9b55 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationType.java
@@ -1,6 +1,6 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
-public enum HsOfficeRelationshipType {
+public enum HsOfficeRelationType {
UNKNOWN,
PARTNER,
EX_PARTNER,
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java
deleted file mode 100644
index d34caa8c..00000000
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepository.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
-
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.Repository;
-
-import jakarta.validation.constraints.NotNull;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-
-public interface HsOfficeRelationshipRepository extends Repository {
-
- Optional findByUuid(UUID id);
-
- default List findRelationshipRelatedToPersonUuidAndRelationshipType(@NotNull UUID personUuid, HsOfficeRelationshipType relationshipType) {
- return findRelationshipRelatedToPersonUuidAndRelationshipTypeString(personUuid, relationshipType.toString());
- }
-
- @Query(value = """
- SELECT p.* FROM hs_office_relationship_rv AS p
- WHERE p.relAnchorUuid = :personUuid OR p.relHolderUuid = :personUuid
- """, nativeQuery = true)
- List findRelationshipRelatedToPersonUuid(@NotNull UUID personUuid);
-
- @Query(value = """
- SELECT p.* FROM hs_office_relationship_rv AS p
- WHERE (:relationshipType IS NULL OR p.relType = cast(:relationshipType AS HsOfficeRelationshipType))
- AND ( p.relAnchorUuid = :personUuid OR p.relHolderUuid = :personUuid)
- """, nativeQuery = true)
- List findRelationshipRelatedToPersonUuidAndRelationshipTypeString(@NotNull UUID personUuid, String relationshipType);
-
- HsOfficeRelationshipEntity save(final HsOfficeRelationshipEntity entity);
-
- long count();
-
- int deleteByUuid(UUID uuid);
-}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index 7fcef622..1b8135ba 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -6,7 +6,7 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -99,7 +99,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.withIdentityView(projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("reference", "agreement", "validity")
- .importEntityAlias("debitorRel", HsOfficeRelationshipEntity.class, dependsOnColumn("debitorRelUuid"))
+ .importEntityAlias("debitorRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorRelUuid"))
.importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
.createRole(OWNER, (with) -> {
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
index 28d29365..2d5cd93c 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
@@ -11,7 +11,7 @@ import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
@@ -804,7 +804,7 @@ public class RbacView {
HsOfficePartnerDetailsEntity.class,
HsOfficeBankAccountEntity.class,
HsOfficeDebitorEntity.class,
- HsOfficeRelationshipEntity.class,
+ HsOfficeRelationEntity.class,
HsOfficeCoopAssetsTransactionEntity.class,
HsOfficeContactEntity.class,
HsOfficeSepaMandateEntity.class,
diff --git a/src/main/resources/api-definition/hs-office/api-mappings.yaml b/src/main/resources/api-definition/hs-office/api-mappings.yaml
index 11778eb0..2403e1e4 100644
--- a/src/main/resources/api-definition/hs-office/api-mappings.yaml
+++ b/src/main/resources/api-definition/hs-office/api-mappings.yaml
@@ -23,7 +23,7 @@ map:
null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/persons/{personUUID}:
null: org.openapitools.jackson.nullable.JsonNullable
- /api/hs/office/relationships/{relationshipUUID}:
+ /api/hs/office/relations/{relationUUID}:
null: org.openapitools.jackson.nullable.JsonNullable
/api/hs/office/bankaccounts/{bankAccountUUID}:
null: org.openapitools.jackson.nullable.JsonNullable
diff --git a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
index a473bd49..eb544c8d 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
@@ -96,8 +96,8 @@ components:
format: int8
minimum: 10000
maximum: 99999
- partnerRole:
- $ref: '#/components/schemas/HsOfficePartnerRoleInsert'
+ partnerRel:
+ $ref: '#/components/schemas/HsOfficePartnerRelInsert'
personUuid:
type: string
format: uuid
@@ -112,22 +112,22 @@ components:
- contactUuid
- details
- HsOfficePartnerRoleInsert:
+ HsOfficePartnerRelInsert:
type: object
nullable: false
properties:
- relAnchorUuid:
+ anchorUuid:
type: string
format: uuid
- relHolderUuid:
+ holderUuid:
type: string
format: uuid
contactUuid:
type: string
format: uuid
required:
- - relAnchorUuid
- - relHolderUuid
+ - anchorUuid
+ - holderUuid
- relContactUuid
HsOfficePartnerDetailsInsert:
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
similarity index 73%
rename from src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml
rename to src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
index af5e5f86..b092cd0a 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
@@ -3,37 +3,37 @@ components:
schemas:
- HsOfficeRelationshipType:
+ HsOfficeRelationType:
type: string
enum:
- UNKNOWN
- PARTNER
- EX_PARTNER
- - REPRESENTATIVE,
+ - DEBITOR
+ - REPRESENTATIVE
- VIP_CONTACT
- - ACCOUNTING,
- OPERATIONS
- SUBSCRIBER
- HsOfficeRelationship:
+ HsOfficeRelation:
type: object
properties:
uuid:
type: string
format: uuid
- relAnchor:
+ anchor:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
- relHolder:
+ holder:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
- relType:
+ type:
type: string
- relMark:
+ mark:
type: string
nullable: true
contact:
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
- HsOfficeRelationshipPatch:
+ HsOfficeRelationPatch:
type: object
properties:
contactUuid:
@@ -41,25 +41,25 @@ components:
format: uuid
nullable: true
- HsOfficeRelationshipInsert:
+ HsOfficeRelationInsert:
type: object
properties:
- relAnchorUuid:
+ anchorUuid:
type: string
format: uuid
- relHolderUuid:
+ holderUuid:
type: string
format: uuid
- relType:
+ type:
type: string
nullable: true
- relMark:
+ mark:
type: string
contactUuid:
type: string
format: uuid
required:
- - relAnchorUuid
- - relHolderUuid
- - relType
+ - anchorUuid
+ - holderUuid
+ - type
- relContactUuid
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationships-with-uuid.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
similarity index 64%
rename from src/main/resources/api-definition/hs-office/hs-office-relationships-with-uuid.yaml
rename to src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
index d3b9605e..4511b895 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relationships-with-uuid.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
@@ -1,25 +1,25 @@
get:
tags:
- - hs-office-relationships
- description: 'Fetch a single person relationship by its uuid, if visible for the current subject.'
- operationId: getRelationshipByUuid
+ - hs-office-relations
+ description: 'Fetch a single person relation by its uuid, if visible for the current subject.'
+ operationId: getRelationByUuid
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- - name: relationshipUUID
+ - name: relationUUID
in: path
required: true
schema:
type: string
format: uuid
- description: UUID of the relationship to fetch.
+ description: UUID of the relation to fetch.
responses:
"200":
description: OK
content:
'application/json':
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
@@ -28,13 +28,13 @@ get:
patch:
tags:
- - hs-office-relationships
- description: 'Updates a single person relationship by its uuid, if permitted for the current subject.'
- operationId: patchRelationship
+ - hs-office-relations
+ description: 'Updates a single person relation by its uuid, if permitted for the current subject.'
+ operationId: patchRelation
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- - name: relationshipUUID
+ - name: relationUUID
in: path
required: true
schema:
@@ -44,14 +44,14 @@ patch:
content:
'application/json':
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipPatch'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
responses:
"200":
description: OK
content:
'application/json':
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
@@ -59,19 +59,19 @@ patch:
delete:
tags:
- - hs-office-relationships
- description: 'Delete a single person relationship by its uuid, if permitted for the current subject.'
- operationId: deleteRelationshipByUuid
+ - hs-office-relations
+ description: 'Delete a single person relation by its uuid, if permitted for the current subject.'
+ operationId: deleteRelationByUuid
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
- - name: relationshipUUID
+ - name: relationUUID
in: path
required: true
schema:
type: string
format: uuid
- description: UUID of the relationship to delete.
+ description: UUID of the relation to delete.
responses:
"204":
description: No Content
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationships.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations.yaml
similarity index 61%
rename from src/main/resources/api-definition/hs-office/hs-office-relationships.yaml
rename to src/main/resources/api-definition/hs-office/hs-office-relations.yaml
index 2d7ed2fd..6328974f 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relationships.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations.yaml
@@ -1,9 +1,9 @@
get:
- summary: Returns a list of (optionally filtered) person relationships for a given person.
- description: Returns the list of (optionally filtered) person relationships of a given person and which are visible to the current user or any of it's assumed roles.
+ summary: Returns a list of (optionally filtered) person relations for a given person.
+ description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current user or any of it's assumed roles.
tags:
- - hs-office-relationships
- operationId: listRelationships
+ - hs-office-relations
+ operationId: listRelations
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
@@ -13,13 +13,13 @@ get:
schema:
type: string
format: uuid
- description: Prefix of name properties from relHolder or contact to filter the results.
- - name: relationshipType
+ description: Prefix of name properties from holder or contact to filter the results.
+ - name: relationType
in: query
required: false
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipType'
- description: Prefix of name properties from relHolder or contact to filter the results.
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationType'
+ description: Prefix of name properties from holder or contact to filter the results.
responses:
"200":
description: OK
@@ -28,17 +28,17 @@ get:
schema:
type: array
items:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
$ref: './error-responses.yaml#/components/responses/Forbidden'
post:
- summary: Adds a new person relationship.
+ summary: Adds a new person relation.
tags:
- - hs-office-relationships
- operationId: addRelationship
+ - hs-office-relations
+ operationId: addRelation
parameters:
- $ref: './auth.yaml#/components/parameters/currentUser'
- $ref: './auth.yaml#/components/parameters/assumedRoles'
@@ -46,7 +46,7 @@ post:
content:
'application/json':
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationshipInsert'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
required: true
responses:
"201":
@@ -54,7 +54,7 @@ post:
content:
'application/json':
schema:
- $ref: './hs-office-relationship-schemas.yaml#/components/schemas/HsOfficeRelationship'
+ $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
diff --git a/src/main/resources/api-definition/hs-office/hs-office.yaml b/src/main/resources/api-definition/hs-office/hs-office.yaml
index f3110867..3bbc5c34 100644
--- a/src/main/resources/api-definition/hs-office/hs-office.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office.yaml
@@ -35,13 +35,13 @@ paths:
$ref: "./hs-office-persons-with-uuid.yaml"
- # Relationships
+ # Relations
- /api/hs/office/relationships:
- $ref: "./hs-office-relationships.yaml"
+ /api/hs/office/relations:
+ $ref: "./hs-office-relations.yaml"
- /api/hs/office/relationships/{relationshipUUID}:
- $ref: "./hs-office-relationships-with-uuid.yaml"
+ /api/hs/office/relations/{relationUUID}:
+ $ref: "./hs-office-relations-with-uuid.yaml"
# BankAccounts
diff --git a/src/main/resources/db/changelog/220-hs-office-relation.sql b/src/main/resources/db/changelog/220-hs-office-relation.sql
new file mode 100644
index 00000000..8e6e56a1
--- /dev/null
+++ b/src/main/resources/db/changelog/220-hs-office-relation.sql
@@ -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');
+--//
diff --git a/src/main/resources/db/changelog/220-hs-office-relationship.sql b/src/main/resources/db/changelog/220-hs-office-relationship.sql
deleted file mode 100644
index a2abece1..00000000
--- a/src/main/resources/db/changelog/220-hs-office-relationship.sql
+++ /dev/null
@@ -1,36 +0,0 @@
---liquibase formatted sql
-
--- ============================================================================
---changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-
-CREATE TYPE HsOfficeRelationshipType AS ENUM (
- 'UNKNOWN',
- 'PARTNER',
- 'EX_PARTNER',
- 'REPRESENTATIVE',
- 'DEBITOR',
- 'VIP_CONTACT',
- 'OPERATIONS',
- 'SUBSCRIBER');
-
-CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT;
-
-create table if not exists hs_office_relationship
-(
- uuid uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade
- relAnchorUuid uuid not null references hs_office_person(uuid),
- relHolderUuid uuid not null references hs_office_person(uuid),
- contactUuid uuid references hs_office_contact(uuid),
- relType HsOfficeRelationshipType not null,
- relMark varchar(24)
-);
---//
-
-
--- ============================================================================
---changeset hs-office-relationship-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-
-call create_journal('hs_office_relationship');
---//
diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
similarity index 64%
rename from src/main/resources/db/changelog/223-hs-office-relationship-rbac.md
rename to src/main/resources/db/changelog/223-hs-office-relation-rbac.md
index 8ffa55ff..40691f38 100644
--- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.md
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
@@ -1,4 +1,4 @@
-### hs_office_relationship RBAC
+### hs_office_relation RBAC
```mermaid
@@ -28,17 +28,17 @@ subgraph hsOfficePerson
--> role:hsOfficePerson.guest[person.guest]
end
-subgraph hsOfficeRelationship
+subgraph hsOfficeRelation
- role:hsOfficePerson#relAnchor.admin[person#anchor.admin]
+ role:hsOfficePerson#anchor.admin[person#anchor.admin]
--- role:hsOfficePerson.admin
- role:hsOfficeRelationship.owner[relationship.owner]
+ role:hsOfficeRelation.owner[relation.owner]
%% permissions
- role:hsOfficeRelationship.owner --> perm:hsOfficeRelationship.*{{relationship.*}}
+ role:hsOfficeRelation.owner --> perm:hsOfficeRelation.*{{relation.*}}
%% incoming
- role:global.admin ---> role:hsOfficeRelationship.owner
- role:hsOfficePersonAdmin#relAnchor.admin
+ role:global.admin ---> role:hsOfficeRelation.owner
+ role:hsOfficePersonAdmin#anchor.admin
end
```
diff --git a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
similarity index 54%
rename from src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql
rename to src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
index 126664a4..6a7d55a1 100644
--- a/src/main/resources/db/changelog/223-hs-office-relationship-rbac.sql
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
@@ -1,97 +1,97 @@
--liquibase formatted sql
-- ============================================================================
---changeset hs-office-relationship-rbac-OBJECT:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRelatedRbacObject('hs_office_relationship');
+call generateRelatedRbacObject('hs_office_relation');
--//
-- ============================================================================
---changeset hs-office-relationship-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRoleDescriptors('hsOfficeRelationship', 'hs_office_relationship');
+call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
--//
-- ============================================================================
---changeset hs-office-relationship-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-ROLES-CREATION:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for relationship entities.
+ Creates and updates the roles and their assignments for relation entities.
*/
-create or replace function hsOfficeRelationshipRbacRolesTrigger()
+create or replace function hsOfficeRelationRbacRolesTrigger()
returns trigger
language plpgsql
strict as $$
declare
- hsOfficeRelationshipTenant RbacRoleDescriptor;
- newRelAnchor hs_office_person;
- newRelHolder hs_office_person;
+ hsOfficeRelationTenant RbacRoleDescriptor;
+ newAnchor hs_office_person;
+ newHolder hs_office_person;
oldContact hs_office_contact;
newContact hs_office_contact;
begin
call enterTriggerForObjectUuid(NEW.uuid);
- hsOfficeRelationshipTenant := hsOfficeRelationshipTenant(NEW);
+ hsOfficeRelationTenant := hsOfficeRelationTenant(NEW);
- select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newRelAnchor;
- select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newRelHolder;
+ select * from hs_office_person as p where p.uuid = NEW.anchorUuid into newAnchor;
+ select * from hs_office_person as p where p.uuid = NEW.holderUuid into newHolder;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
if TG_OP = 'INSERT' then
perform createRoleWithGrants(
- hsOfficeRelationshipOwner(NEW),
+ hsOfficeRelationOwner(NEW),
permissions => array['DELETE'],
incomingSuperRoles => array[
globalAdmin(),
- hsOfficePersonAdmin(newRelAnchor)]
+ hsOfficePersonAdmin(newAnchor)]
);
perform createRoleWithGrants(
- hsOfficeRelationshipAdmin(NEW),
+ hsOfficeRelationAdmin(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
+ incomingSuperRoles => array[hsOfficeRelationOwner(NEW)]
);
-- the tenant role for those related users who can view the data
perform createRoleWithGrants(
- hsOfficeRelationshipTenant,
+ hsOfficeRelationTenant,
permissions => array['SELECT'],
incomingSuperRoles => array[
- hsOfficeRelationshipAdmin(NEW),
- hsOfficePersonAdmin(newRelAnchor),
- hsOfficePersonAdmin(newRelHolder),
+ hsOfficeRelationAdmin(NEW),
+ hsOfficePersonAdmin(newAnchor),
+ hsOfficePersonAdmin(newHolder),
hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[
- hsOfficePersonTenant(newRelAnchor),
- hsOfficePersonTenant(newRelHolder),
+ hsOfficePersonTenant(newAnchor),
+ hsOfficePersonTenant(newHolder),
hsOfficeContactTenant(newContact)]
);
-- anchor and holder admin roles need each others tenant role
- -- to be able to see the joined relationship
+ -- to be able to see the joined relation
-- TODO: this can probably be avoided through agent+guest roles
- call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
- call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
- call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
+ call grantRoleToRole(hsOfficePersonTenant(newAnchor), hsOfficePersonAdmin(newHolder));
+ call grantRoleToRole(hsOfficePersonTenant(newHolder), hsOfficePersonAdmin(newAnchor));
+ call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newHolder), hsOfficeContactAdmin(newContact));
elsif TG_OP = 'UPDATE' then
if OLD.contactUuid <> NEW.contactUuid then
-- nothing but the contact can be updated,
- -- in other cases, a new relationship needs to be created and the old updated
+ -- in other cases, a new relation needs to be created and the old updated
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
- call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
- call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
+ call revokeRoleFromRole( hsOfficeRelationTenant, hsOfficeContactAdmin(oldContact) );
+ call grantRoleToRole( hsOfficeRelationTenant, hsOfficeContactAdmin(newContact) );
- call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
- call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
+ call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationTenant );
+ call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationTenant );
end if;
else
raise exception 'invalid usage of TRIGGER';
@@ -104,39 +104,39 @@ end; $$;
/*
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
*/
-create trigger createRbacRolesForHsOfficeRelationship_Trigger
+create trigger createRbacRolesForHsOfficeRelation_Trigger
after insert
- on hs_office_relationship
+ on hs_office_relation
for each row
-execute procedure hsOfficeRelationshipRbacRolesTrigger();
+execute procedure hsOfficeRelationRbacRolesTrigger();
/*
An AFTER UPDATE TRIGGER which updates the role structure of a customer.
*/
-create trigger updateRbacRolesForHsOfficeRelationship_Trigger
+create trigger updateRbacRolesForHsOfficeRelation_Trigger
after update
- on hs_office_relationship
+ on hs_office_relation
for each row
-execute procedure hsOfficeRelationshipRbacRolesTrigger();
+execute procedure hsOfficeRelationRbacRolesTrigger();
--//
-- ============================================================================
---changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
- (select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
- || '-with-' || target.relType || '-' ||
- (select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
+call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$
+ (select idName from hs_office_person_iv p where p.uuid = target.anchorUuid)
+ || '-with-' || target.type || '-' ||
+ (select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
$idName$);
--//
-- ============================================================================
---changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_relationship',
- '(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
+call generateRbacRestrictedView('hs_office_relation',
+ '(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)',
$updates$
contactUuid = new.contactUuid
$updates$);
@@ -146,10 +146,10 @@ call generateRbacRestrictedView('hs_office_relationship',
-- ============================================================================
---changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates a global permission for new-relationship and assigns it to the hostsharing admins role.
+ Creates a global permission for new-relation and assigns it to the hostsharing admins role.
*/
do language plpgsql $$
declare
@@ -157,11 +157,11 @@ do language plpgsql $$
globalObjectUuid uuid;
globalAdminRoleUuid uuid ;
begin
- call defineContext('granting global new-relationship permission to global admin role', null, null, null);
+ call defineContext('granting global new-relation permission to global admin role', null, null, null);
globalAdminRoleUuid := findRoleId(globalAdmin());
globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']);
+ addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relation']);
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
end;
$$;
@@ -169,24 +169,24 @@ $$;
/**
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
*/
-create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects()
+create or replace function addHsOfficeRelationNotAllowedForCurrentSubjects()
returns trigger
language PLPGSQL
as $$
begin
- raise exception '[403] new-relationship not permitted for %',
+ raise exception '[403] new-relation not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**
Checks if the user or assumed roles are allowed to create a new customer.
*/
-create trigger hs_office_relationship_insert_trigger
+create trigger hs_office_relation_insert_trigger
before insert
- on hs_office_relationship
+ on hs_office_relation
for each row
- -- TODO.spec: who is allowed to create new relationships
+ -- TODO.spec: who is allowed to create new relations
when ( not hasAssumedRole() )
-execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects();
+execute procedure addHsOfficeRelationNotAllowedForCurrentSubjects();
--//
diff --git a/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
similarity index 57%
rename from src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql
rename to src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
index 39c15ac2..8ad39359 100644
--- a/src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql
+++ b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
@@ -2,15 +2,15 @@
-- ============================================================================
---changeset hs-office-relationship-TEST-DATA-GENERATOR:1 endDelimiter:--//
+--changeset hs-office-relation-TEST-DATA-GENERATOR:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates a single relationship test record.
+ Creates a single relation test record.
*/
-create or replace procedure createHsOfficeRelationshipTestData(
+create or replace procedure createHsOfficeRelationTestData(
holderPersonName varchar,
- relationshipType HsOfficeRelationshipType,
+ relationType HsOfficeRelationType,
anchorPersonTradeName varchar,
contactLabel varchar,
mark varchar default null)
@@ -24,7 +24,7 @@ declare
begin
idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName);
- currentTask := 'creating relationship test-data ' || idName;
+ 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);
@@ -45,20 +45,20 @@ begin
raise exception 'contact "%" not found', contactLabel;
end if;
- raise notice 'creating test relationship: %', idName;
+ 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_relationship (uuid, relanchoruuid, relholderuuid, reltype, relmark, contactUuid)
- values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationshipType, mark, contact.uuid);
+ 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 relationship for mass data generation.
+ Creates a range of test relation for mass data generation.
*/
-create or replace procedure createHsOfficeRelationshipTestData(
+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
)
@@ -72,7 +72,7 @@ begin
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 createHsOfficeRelationshipTestData(person.uuid, contact.uuid, 'REPRESENTATIVE');
+ call createHsOfficeRelationTestData(person.uuid, contact.uuid, 'REPRESENTATIVE');
commit;
end loop;
end; $$;
@@ -80,25 +80,25 @@ end; $$;
-- ============================================================================
---changeset hs-office-relationship-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--//
+--changeset hs-office-relation-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--//
-- ----------------------------------------------------------------------------
do language plpgsql $$
begin
- call createHsOfficeRelationshipTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
- call createHsOfficeRelationshipTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
+ call createHsOfficeRelationTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
+ call createHsOfficeRelationTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
- call createHsOfficeRelationshipTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
- call createHsOfficeRelationshipTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
+ call createHsOfficeRelationTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
+ call createHsOfficeRelationTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
- call createHsOfficeRelationshipTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
- call createHsOfficeRelationshipTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
+ call createHsOfficeRelationTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
+ call createHsOfficeRelationTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
- call createHsOfficeRelationshipTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
- call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
+ call createHsOfficeRelationTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
+ call createHsOfficeRelationTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
- call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
- call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
+ call createHsOfficeRelationTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
+ call createHsOfficeRelationTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
end;
$$;
--//
diff --git a/src/main/resources/db/changelog/230-hs-office-partner.sql b/src/main/resources/db/changelog/230-hs-office-partner.sql
index d1db4400..73a02fa1 100644
--- a/src/main/resources/db/changelog/230-hs-office-partner.sql
+++ b/src/main/resources/db/changelog/230-hs-office-partner.sql
@@ -33,9 +33,9 @@ create table hs_office_partner
(
uuid uuid unique references RbacObject (uuid) initially deferred,
partnerNumber numeric(5) unique not null,
- partnerRoleUuid uuid not null references hs_office_relationship(uuid), -- TODO: delete in after delete trigger
- personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid
- contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid
+ partnerRelUuid uuid not null references hs_office_relation(uuid), -- TODO: delete in after delete trigger
+ personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRelUuid
+ contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRelUuid
detailsUuid uuid not null references hs_office_partner_details(uuid) -- deleted in after delete trigger
);
--//
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
index d16048fd..c2882dbb 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
@@ -27,8 +27,8 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql
strict as $$
declare
- oldPartnerRole hs_office_relationship;
- newPartnerRole hs_office_relationship;
+ oldPartnerRel hs_office_relation;
+ newPartnerRel hs_office_relation;
oldPerson hs_office_person;
newPerson hs_office_person;
@@ -38,7 +38,7 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
- select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRole;
+ select * from hs_office_relation as r where r.uuid = NEW.partnerReluuid into newPartnerRel;
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
@@ -58,7 +58,7 @@ begin
incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[
- hsOfficeRelationshipTenant(newPartnerRole),
+ hsOfficeRelationTenant(newPartnerRel),
hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)]
);
@@ -67,7 +67,7 @@ begin
hsOfficePartnerAgent(NEW),
incomingSuperRoles => array[
hsOfficePartnerAdmin(NEW),
- hsOfficeRelationshipAdmin(newPartnerRole),
+ hsOfficeRelationAdmin(newPartnerRel),
hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)]
);
@@ -77,7 +77,7 @@ begin
incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[
- hsOfficeRelationshipTenant(newPartnerRole),
+ hsOfficeRelationTenant(newPartnerRel),
hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)]
);
@@ -118,17 +118,17 @@ begin
elsif TG_OP = 'UPDATE' then
- if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then
- select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole;
+ if OLD.partnerRelUuid <> NEW.partnerRelUuid then
+ select * from hs_office_relation as r where r.uuid = OLD.partnerRelUuid into oldPartnerRel;
- call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRole), hsOfficePartnerAdmin(OLD));
- call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRole), hsOfficePartnerAdmin(NEW));
+ call revokeRoleFromRole(hsOfficeRelationTenant(oldPartnerRel), hsOfficePartnerAdmin(OLD));
+ call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficePartnerAdmin(NEW));
- call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationshipAdmin(oldPartnerRole));
- call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationshipAdmin(newPartnerRole));
+ call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationAdmin(oldPartnerRel));
+ call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationAdmin(newPartnerRel));
- call revokeRoleFromRole(hsOfficeRelationshipGuest(oldPartnerRole), hsOfficePartnerTenant(OLD));
- call grantRoleToRole(hsOfficeRelationshipGuest(newPartnerRole), hsOfficePartnerTenant(NEW));
+ call revokeRoleFromRole(hsOfficeRelationGuest(oldPartnerRel), hsOfficePartnerTenant(OLD));
+ call grantRoleToRole(hsOfficeRelationGuest(newPartnerRel), hsOfficePartnerTenant(NEW));
end if;
if OLD.personUuid <> NEW.personUuid then
@@ -202,7 +202,7 @@ call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
call generateRbacRestrictedView('hs_office_partner',
'(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
$updates$
- partnerRoleUuid = new.partnerRoleUuid,
+ partnerRelUuid = new.partnerRelUuid,
personUuid = new.personUuid,
contactUuid = new.contactUuid
$updates$);
diff --git a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
index 146f2f1d..765803a8 100644
--- a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
+++ b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
@@ -18,7 +18,7 @@ declare
currentTask varchar;
idName varchar;
mandantPerson hs_office_person;
- partnerRole hs_office_relationship;
+ partnerRel hs_office_relation;
relatedPerson hs_office_person;
relatedContact hs_office_contact;
relatedDetailsUuid uuid;
@@ -42,16 +42,16 @@ begin
where c.label = contactLabel
into relatedContact;
- select r.* from hs_office_relationship r
- where r.reltype = 'PARTNER'
- and r.relanchoruuid = mandantPerson.uuid and r.relholderuuid = relatedPerson.uuid
- into partnerRole;
- if partnerRole is null then
- raise exception 'partnerRole "%"-"%" not found', mandantPerson.tradename, partnerPersonName;
+ 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 partnerRole (%): %', partnerRole.uuid, partnerRole;
+ raise notice '- using partnerRel (%): %', partnerRel.uuid, partnerRel;
raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson;
raise notice '- using contact (%): %', relatedContact.uuid, relatedContact;
@@ -68,8 +68,8 @@ begin
end if;
insert
- into hs_office_partner (uuid, partnerNumber, partnerRoleUuid, personuuid, contactuuid, detailsUuid)
- values (uuid_generate_v4(), partnerNumber, partnerRole.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
+ into hs_office_partner (uuid, partnerNumber, partnerRelUuid, personuuid, contactuuid, detailsUuid)
+ values (uuid_generate_v4(), partnerNumber, partnerRel.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
end; $$;
--//
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 2b8417c3..5934c9a4 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -68,11 +68,11 @@ databaseChangeLog:
- include:
file: db/changelog/218-hs-office-person-test-data.sql
- include:
- file: db/changelog/220-hs-office-relationship.sql
+ file: db/changelog/220-hs-office-relation.sql
- include:
- file: db/changelog/223-hs-office-relationship-rbac.sql
+ file: db/changelog/223-hs-office-relation-rbac.sql
- include:
- file: db/changelog/228-hs-office-relationship-test-data.sql
+ file: db/changelog/228-hs-office-relation-test-data.sql
- include:
file: db/changelog/230-hs-office-partner.sql
- include:
diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
index 013b2309..fa49e102 100644
--- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
@@ -41,7 +41,7 @@ public class ArchitectureTest {
"..hs.office.migration",
"..hs.office.partner",
"..hs.office.person",
- "..hs.office.relationship",
+ "..hs.office.relation",
"..hs.office.sepamandate",
"..errors",
"..mapper",
@@ -148,7 +148,7 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeContactPackageRule = classes()
.that().resideInAPackage("..hs.office.contact..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.contact..", "..hs.office.relationship..",
+ .resideInAnyPackage("..hs.office.contact..", "..hs.office.relation..",
"..hs.office.partner..",
"..hs.office.debitor..",
"..hs.office.membership..",
@@ -159,7 +159,7 @@ public class ArchitectureTest {
public static final ArchRule hsOfficePersonPackageRule = classes()
.that().resideInAPackage("..hs.office.person..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.person..", "..hs.office.relationship..",
+ .resideInAnyPackage("..hs.office.person..", "..hs.office.relation..",
"..hs.office.partner..",
"..hs.office.debitor..",
"..hs.office.membership..",
@@ -167,10 +167,10 @@ public class ArchitectureTest {
@ArchTest
@SuppressWarnings("unused")
- public static final ArchRule hsOfficeRelationshipPackageRule = classes()
- .that().resideInAPackage("..hs.office.relationship..")
+ public static final ArchRule hsOfficeRelationPackageRule = classes()
+ .that().resideInAPackage("..hs.office.relation..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.relationship..",
+ .resideInAnyPackage("..hs.office.relation..",
"..hs.office.partner..",
"..hs.office.migration..");
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index 0c86dc66..c78ad519 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -18,8 +18,8 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.test.JpaAttempt;
@@ -127,7 +127,7 @@ public class ImportOfficeData extends ContextBasedTest {
new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"},
SUBSCRIBER_ROLES);
- static int relationshipId = 2000000;
+ static int relationId = 2000000;
@Value("${spring.datasource.url}")
private String jdbcUrl;
@@ -144,7 +144,7 @@ public class ImportOfficeData extends ContextBasedTest {
private static Map debitors = new WriteOnceMap<>();
private static Map memberships = new WriteOnceMap<>();
- private static Map relationships = new WriteOnceMap<>();
+ private static Map relations = new WriteOnceMap<>();
private static Map sepaMandates = new WriteOnceMap<>();
private static Map bankAccounts = new WriteOnceMap<>();
private static Map coopShares = new WriteOnceMap<>();
@@ -220,17 +220,17 @@ public class ImportOfficeData extends ContextBasedTest {
@Test
@Order(1021)
- void buildDebitorRelationships() {
+ void buildDebitorRelations() {
debitors.forEach( (id, debitor) -> {
- final var debitorRel = HsOfficeRelationshipEntity.builder()
- .relType(HsOfficeRelationshipType.DEBITOR)
- .relAnchor(debitor.getPartner().getPartnerRole().getRelHolder())
- .relHolder(debitor.getPartner().getPartnerRole().getRelHolder()) // just 1 debitor/partner in legacy hsadmin
+ final var debitorRel = HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(debitor.getPartner().getPartnerRel().getHolder())
+ .holder(debitor.getPartner().getPartnerRel().getHolder()) // just 1 debitor/partner in legacy hsadmin
.contact(debitor.getBillingContact())
.build();
- if (debitorRel.getRelAnchor() != null && debitorRel.getRelHolder() != null &&
+ if (debitorRel.getAnchor() != null && debitorRel.getHolder() != null &&
debitorRel.getContact() != null ) {
- relationships.put(relationshipId++, debitorRel);
+ relations.put(relationId++, debitorRel);
}
});
}
@@ -288,28 +288,28 @@ public class ImportOfficeData extends ContextBasedTest {
22=Membership(M-1102200, ?? Test PS, D-1102200, [2021-04-01,), NONE)
}
""");
- assertThat(toFormattedString(relationships)).isEqualToIgnoringWhitespace("""
+ assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace("""
{
- 2000000=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000001=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000002=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000003=rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='null null, null'),
- 2000004=rel(relAnchor='NP Mellies, Michael', relType='OPERATIONS', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000005=rel(relAnchor='NP Mellies, Michael', relType='REPRESENTATIVE', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000006=rel(relAnchor='LP JM GmbH', relType='EX_PARTNER', relHolder='LP JM e.K.', contact='JM e.K.'),
- 2000007=rel(relAnchor='LP JM GmbH', relType='OPERATIONS', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000008=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000009=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='operations-announce', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000010=rel(relAnchor='LP JM GmbH', relType='REPRESENTATIVE', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000011=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='members-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000012=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='customers-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000013=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
- 2000014=rel(relAnchor='?? Test PS', relType='OPERATIONS', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000015=rel(relAnchor='?? Test PS', relType='REPRESENTATIVE', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000016=rel(relAnchor='NP Mellies, Michael', relType='SUBSCRIBER', relMark='operations-announce', relHolder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
- 2000017=rel(relAnchor='NP Mellies, Michael', relType='DEBITOR', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000018=rel(relAnchor='LP JM GmbH', relType='DEBITOR', relHolder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
- 2000019=rel(relAnchor='?? Test PS', relType='DEBITOR', relHolder='?? Test PS', contact='Petra Schmidt , Test PS')
+ 2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000001=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000002=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000003=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
+ 2000004=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000005=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000006=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
+ 2000007=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000008=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000009=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000010=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000011=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000012=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000013=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
+ 2000014=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000015=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000016=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
+ 2000017=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000018=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
+ 2000019=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS')
}
""");
}
@@ -419,20 +419,20 @@ public class ImportOfficeData extends ContextBasedTest {
@Test
@Order(2009)
- void removeEmptyRelationships() {
+ void removeEmptyRelations() {
assumeThatWeAreImportingControlledTestData();
// avoid a error when persisting the deliberetely invalid partner entry #99
final var idsToRemove = new HashSet();
- relationships.forEach( (id, r) -> {
+ relations.forEach( (id, r) -> {
// such a record
if (r.getContact() == null || r.getContact().getLabel() == null ||
- r.getRelHolder() == null | r.getRelHolder().getPersonType() == null ) {
+ r.getHolder() == null | r.getHolder().getPersonType() == null ) {
idsToRemove.add(id);
}
});
assertThat(idsToRemove.size()).isEqualTo(1); // only from partner #99 (partner+contractual roles)
- idsToRemove.forEach(id -> relationships.remove(id));
+ idsToRemove.forEach(id -> relations.remove(id));
}
@Test
@@ -495,7 +495,7 @@ public class ImportOfficeData extends ContextBasedTest {
jpaAttempt.transacted(() -> {
context(rbacSuperuser);
- relationships.forEach(this::persist);
+ relations.forEach(this::persist);
}).assertSuccessful();
jpaAttempt.transacted(() -> {
@@ -572,7 +572,7 @@ public class ImportOfficeData extends ContextBasedTest {
em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate();
em.createNativeQuery("delete from hs_office_partner where true").executeUpdate();
em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate();
- em.createNativeQuery("delete from hs_office_relationship where true").executeUpdate();
+ em.createNativeQuery("delete from hs_office_relation where true").executeUpdate();
em.createNativeQuery("delete from hs_office_contact where true").executeUpdate();
em.createNativeQuery("delete from hs_office_person where true").executeUpdate();
}).assertSuccessful();
@@ -676,18 +676,18 @@ public class ImportOfficeData extends ContextBasedTest {
.forEach(rec -> {
final var person = HsOfficePersonEntity.builder().build();
- final var partnerRelationship = HsOfficeRelationshipEntity.builder()
- .relHolder(person)
- .relType(HsOfficeRelationshipType.PARTNER)
- .relAnchor(mandant)
+ final var partnerRelation = HsOfficeRelationEntity.builder()
+ .holder(person)
+ .type(HsOfficeRelationType.PARTNER)
+ .anchor(mandant)
.contact(null) // is set during contacts import depending on assigned roles
.build();
- relationships.put(relationshipId++, partnerRelationship);
+ relations.put(relationId++, partnerRelation);
final var partner = HsOfficePartnerEntity.builder()
.partnerNumber(rec.getInteger("member_id"))
.details(HsOfficePartnerDetailsEntity.builder().build())
- .partnerRole(partnerRelationship)
+ .partnerRel(partnerRelation)
.contact(null) // is set during contacts import depending on assigned roles
.person(person)
.build();
@@ -845,7 +845,7 @@ public class ImportOfficeData extends ContextBasedTest {
final var debitor = debitors.get(bpId);
final var partnerPerson = partner.getPerson();
- if (containsPartnerRole(rec)) {
+ if (containsPartnerRel(rec)) {
initPerson(partner.getPerson(), rec);
}
@@ -859,46 +859,46 @@ public class ImportOfficeData extends ContextBasedTest {
final var contact = HsOfficeContactEntity.builder().build();
initContact(contact, rec);
- if (containsPartnerRole(rec)) {
+ if (containsPartnerRel(rec)) {
assertThat(partner.getContact()).isNull();
partner.setContact(contact);
- partner.getPartnerRole().setContact(contact);
+ partner.getPartnerRel().setContact(contact);
}
if (containsRole(rec, "billing")) {
assertThat(debitor.getBillingContact()).isNull();
debitor.setBillingContact(contact);
}
if (containsRole(rec, "operation")) {
- addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.OPERATIONS);
+ addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.OPERATIONS);
}
if (containsRole(rec, "contractual")) {
- addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.REPRESENTATIVE);
+ addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.REPRESENTATIVE);
}
if (containsRole(rec, "ex-partner")) {
- addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.EX_PARTNER);
+ addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.EX_PARTNER);
}
if (containsRole(rec, "vip-contact")) {
- addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT);
+ addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.VIP_CONTACT);
}
for (String subscriberRole: SUBSCRIBER_ROLES) {
if (containsRole(rec, subscriberRole)) {
- addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.SUBSCRIBER)
- .setRelMark(subscriberRole.split(":")[1])
+ addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.SUBSCRIBER)
+ .setMark(subscriberRole.split(":")[1])
;
}
}
verifyContainsOnlyKnownRoles(rec.getString("roles"));
});
- optionallyAddMissingContractualRelationships();
+ optionallyAddMissingContractualRelations();
}
- private static void optionallyAddMissingContractualRelationships() {
+ private static void optionallyAddMissingContractualRelations() {
final var contractualMissing = new HashSet();
partners.forEach( (id, partner) -> {
final var partnerPerson = partner.getPerson();
- if (relationships.values().stream()
- .filter(rel -> rel.getRelAnchor() == partnerPerson && rel.getRelType() == HsOfficeRelationshipType.REPRESENTATIVE)
+ if (relations.values().stream()
+ .filter(rel -> rel.getAnchor() == partnerPerson && rel.getType() == HsOfficeRelationType.REPRESENTATIVE)
.findFirst().isEmpty()) {
contractualMissing.add(partner.getPartnerNumber());
}
@@ -909,22 +909,22 @@ public class ImportOfficeData extends ContextBasedTest {
return ("," + roles + ",").contains("," + role + ",");
}
- private static boolean containsPartnerRole(final Record rec) {
+ private static boolean containsPartnerRel(final Record rec) {
return containsRole(rec, "partner");
}
- private static HsOfficeRelationshipEntity addRelationship(
+ private static HsOfficeRelationEntity addRelation(
final HsOfficePersonEntity partnerPerson,
final HsOfficePersonEntity contactPerson,
final HsOfficeContactEntity contact,
- final HsOfficeRelationshipType representative) {
- final var rel = HsOfficeRelationshipEntity.builder()
- .relAnchor(partnerPerson)
- .relHolder(contactPerson)
+ final HsOfficeRelationType representative) {
+ final var rel = HsOfficeRelationEntity.builder()
+ .anchor(partnerPerson)
+ .holder(contactPerson)
.contact(contact)
- .relType(representative)
+ .type(representative)
.build();
- relationships.put(relationshipId++, rel);
+ relations.put(relationId++, rel);
return rel;
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
index 33a312c4..9e712a2d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
@@ -7,9 +7,9 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.test.Accepts;
import net.hostsharing.test.JpaAttempt;
@@ -41,7 +41,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
HsOfficePartnerRepository partnerRepo;
@Autowired
- HsOfficeRelationshipRepository relationshipRepository;
+ HsOfficeRelationRepository relationRepository;
@Autowired
HsOfficePersonRepository personRepo;
@@ -102,9 +102,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("""
{
"partnerNumber": "20002",
- "partnerRole": {
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "partnerRel": {
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
},
"personUuid": "%s",
@@ -155,9 +155,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("""
{
"partnerNumber": "20003",
- "partnerRole": {
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "partnerRel": {
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
},
"personUuid": "%s",
@@ -193,9 +193,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("""
{
"partnerNumber": "20004",
- "partnerRole": {
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "partnerRel": {
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
},
"personUuid": "%s",
@@ -413,7 +413,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
// then the given partner is gone
assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isEmpty();
- assertThat(relationshipRepository.findByUuid(givenPartner.getPartnerRole().getUuid())).isEmpty();
+ assertThat(relationRepository.findByUuid(givenPartner.getPartnerRel().getUuid())).isEmpty();
}
@Test
@@ -465,15 +465,15 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
- final var partnerRole = new HsOfficeRelationshipEntity();
- partnerRole.setRelType(HsOfficeRelationshipType.PARTNER);
- partnerRole.setRelAnchor(givenMandantPerson);
- partnerRole.setRelHolder(givenPerson);
- partnerRole.setContact(givenContact);
- em.persist(partnerRole);
+ final var partnerRel = new HsOfficeRelationEntity();
+ partnerRel.setType(HsOfficeRelationType.PARTNER);
+ partnerRel.setAnchor(givenMandantPerson);
+ partnerRel.setHolder(givenPerson);
+ partnerRel.setContact(givenContact);
+ em.persist(partnerRel);
final var newPartner = HsOfficePartnerEntity.builder()
- .partnerRole(partnerRole)
+ .partnerRel(partnerRel)
.partnerNumber(partnerNumber)
.person(givenPerson)
.contact(givenContact)
@@ -492,6 +492,6 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
cleanupAllNew(HsOfficePartnerEntity.class);
// TODO: should not be necessary anymore, once it's deleted via after delete trigger
- cleanupAllNew(HsOfficeRelationshipEntity.class);
+ cleanupAllNew(HsOfficeRelationEntity.class);
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
index ed04d899..e86cbc94 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
@@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
@@ -54,7 +54,7 @@ class HsOfficePartnerControllerRestTest {
HsOfficePartnerRepository partnerRepo;
@MockBean
- HsOfficeRelationshipRepository relationshipRepo;
+ HsOfficeRelationRepository relationRepo;
@MockBean
EntityManager em;
@@ -100,9 +100,9 @@ class HsOfficePartnerControllerRestTest {
.content("""
{
"partnerNumber": "20002",
- "partnerRole": {
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "partnerRel": {
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
},
"personUuid": "%s",
@@ -137,9 +137,9 @@ class HsOfficePartnerControllerRestTest {
.content("""
{
"partnerNumber": "20002",
- "partnerRole": {
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "partnerRel": {
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
},
"personUuid": "%s",
@@ -175,11 +175,11 @@ class HsOfficePartnerControllerRestTest {
when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock));
when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(0);
- final UUID givenRelationshipUuid = UUID.randomUUID();
- when(partnerMock.getPartnerRole()).thenReturn(HsOfficeRelationshipEntity.builder()
- .uuid(givenRelationshipUuid)
+ final UUID givenRelationUuid = UUID.randomUUID();
+ when(partnerMock.getPartnerRel()).thenReturn(HsOfficeRelationEntity.builder()
+ .uuid(givenRelationUuid)
.build());
- when(relationshipRepo.deleteByUuid(givenRelationshipUuid)).thenReturn(0);
+ when(relationRepo.deleteByUuid(givenRelationUuid)).thenReturn(0);
// when
mockMvc.perform(MockMvcRequestBuilders
@@ -193,18 +193,18 @@ class HsOfficePartnerControllerRestTest {
}
@Test
- void respondBadRequest_ifRelationshipCannotBeDeleted() throws Exception {
+ void respondBadRequest_ifRelationCannotBeDeleted() throws Exception {
// given
final UUID givenPartnerUuid = UUID.randomUUID();
when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock));
when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(1);
- when(relationshipRepo.deleteByUuid(any())).thenReturn(0);
+ when(relationRepo.deleteByUuid(any())).thenReturn(0);
- final UUID givenRelationshipUuid = UUID.randomUUID();
- when(partnerMock.getPartnerRole()).thenReturn(HsOfficeRelationshipEntity.builder()
- .uuid(givenRelationshipUuid)
+ final UUID givenRelationUuid = UUID.randomUUID();
+ when(partnerMock.getPartnerRel()).thenReturn(HsOfficeRelationEntity.builder()
+ .uuid(givenRelationUuid)
.build());
- when(relationshipRepo.deleteByUuid(givenRelationshipUuid)).thenReturn(0);
+ when(relationRepo.deleteByUuid(givenRelationUuid)).thenReturn(0);
// when
mockMvc.perform(MockMvcRequestBuilders
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
index 94d06a77..75eaac3e 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
@@ -3,9 +3,9 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
-import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
@@ -43,7 +43,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
HsOfficePartnerRepository partnerRepo;
@Autowired
- HsOfficeRelationshipRepository relationshipRepo;
+ HsOfficeRelationRepository relationRepo;
@Autowired
HsOfficePersonRepository personRepo;
@@ -80,19 +80,19 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
- final var partnerRole = HsOfficeRelationshipEntity.builder()
- .relHolder(givenPartnerPerson)
- .relType(HsOfficeRelationshipType.PARTNER)
- .relAnchor(givenMandantorPerson)
+ final var partnerRel = HsOfficeRelationEntity.builder()
+ .holder(givenPartnerPerson)
+ .type(HsOfficeRelationType.PARTNER)
+ .anchor(givenMandantorPerson)
.contact(givenContact)
.build();
- relationshipRepo.save(partnerRole);
+ relationRepo.save(partnerRel);
// when
final var result = attempt(em, () -> {
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20031)
- .partnerRole(partnerRole)
+ .partnerRel(partnerRel)
.person(givenPartnerPerson)
.contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder()
@@ -125,17 +125,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
- final var newRelationship = HsOfficeRelationshipEntity.builder()
- .relHolder(givenPartnerPerson)
- .relType(HsOfficeRelationshipType.PARTNER)
- .relAnchor(givenMandantPerson)
+ final var newRelation = HsOfficeRelationEntity.builder()
+ .holder(givenPartnerPerson)
+ .type(HsOfficeRelationType.PARTNER)
+ .anchor(givenMandantPerson)
.contact(givenContact)
.build();
- relationshipRepo.save(newRelationship);
+ relationRepo.save(newRelation);
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20032)
- .partnerRole(newRelationship)
+ .partnerRel(newRelation)
.person(givenPartnerPerson)
.contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build())
@@ -146,9 +146,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
- "hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner",
- "hs_office_relationship#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.admin",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.agent",
"hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.owner",
@@ -160,25 +160,25 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(fromFormatted(
initialGrantNames,
- // relationship - TODO: check and cleanup
+ // relation - TODO: check and cleanup
"{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }",
"{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.tenant by system and assume }",
- "{ grant role partner#20032:EBess-4th.agent to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
- "{ grant perm UPDATE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm DELETE on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.admin to role relationship#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant perm SELECT on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role contact#4th.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#HostsharingeG.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.tenant by system and assume }",
+ "{ grant role partner#20032:EBess-4th.agent to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
+ "{ grant perm UPDATE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm DELETE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
+ "{ grant perm SELECT on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role contact#4th.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role person#EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role person#HostsharingeG.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
// owner
"{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }",
@@ -426,15 +426,15 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- // TODO: should deleting a partner automatically delete the PARTNER relationship? (same for debitor)
+ // TODO: should deleting a partner automatically delete the PARTNER relation? (same for debitor)
// TODO: why did the test cleanup check does not notice this, if missing?
return partnerRepo.deleteByUuid(givenPartner.getUuid()) +
- relationshipRepo.deleteByUuid(givenPartner.getPartnerRole().getUuid());
+ relationRepo.deleteByUuid(givenPartner.getPartnerRel().getUuid());
});
// then
result.assertSuccessful();
- assertThat(result.returnedValue()).isEqualTo(2); // partner+relationship
+ assertThat(result.returnedValue()).isEqualTo(2); // partner+relation
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(initialRoleNames);
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(initialGrantNames);
}
@@ -466,17 +466,17 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
- final var partnerRole = HsOfficeRelationshipEntity.builder()
- .relHolder(givenPartnerPerson)
- .relType(HsOfficeRelationshipType.PARTNER)
- .relAnchor(givenMandantorPerson)
+ final var partnerRel = HsOfficeRelationEntity.builder()
+ .holder(givenPartnerPerson)
+ .type(HsOfficeRelationType.PARTNER)
+ .anchor(givenMandantorPerson)
.contact(givenContact)
.build();
- relationshipRepo.save(partnerRole);
+ relationRepo.save(partnerRel);
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber)
- .partnerRole(partnerRole)
+ .partnerRel(partnerRel)
.person(givenPartnerPerson)
.contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build())
@@ -502,7 +502,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
void cleanup() {
cleanupAllNew(HsOfficePartnerDetailsEntity.class); // TODO: should not be necessary
cleanupAllNew(HsOfficePartnerEntity.class);
- cleanupAllNew(HsOfficeRelationshipEntity.class);
+ cleanupAllNew(HsOfficeRelationEntity.class);
}
private String[] distinct(final String[] strings) {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
similarity index 63%
rename from src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java
rename to src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
index 8f9e9147..fd978e1d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
@@ -1,4 +1,4 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
@@ -7,7 +7,7 @@ import net.hostsharing.test.Accepts;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
-import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipTypeResource;
+import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.test.JpaAttempt;
import org.json.JSONException;
@@ -31,7 +31,7 @@ import static org.hamcrest.Matchers.startsWith;
classes = { HsadminNgApplication.class, JpaAttempt.class }
)
@Transactional
-class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithCleanup {
+class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithCleanup {
public static final UUID GIVEN_NON_EXISTING_HOLDER_PERSON_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
@LocalServerPort
@@ -44,7 +44,7 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
Context contextMock;
@Autowired
- HsOfficeRelationshipRepository relationshipRepo;
+ HsOfficeRelationRepository relationRepo;
@Autowired
HsOfficePersonRepository personRepo;
@@ -56,11 +56,11 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
JpaAttempt jpaAttempt;
@Nested
- @Accepts({ "Relationship:F(Find)" })
- class ListRelationships {
+ @Accepts({ "Relation:F(Find)" })
+ class ListRelations {
@Test
- void globalAdmin_withoutAssumedRoles_canViewAllRelationshipsOfGivenPersonAndType_ifNoCriteriaGiven() throws JSONException {
+ void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType_ifNoCriteriaGiven() throws JSONException {
// given
context.define("superuser-alex@hostsharing.net");
@@ -71,45 +71,45 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
- .get("http://localhost/api/hs/office/relationships?personUuid=%s&relationshipType=%s"
- .formatted(givenPerson.getUuid(), HsOfficeRelationshipTypeResource.PARTNER))
+ .get("http://localhost/api/hs/office/relations?personUuid=%s&relationType=%s"
+ .formatted(givenPerson.getUuid(), HsOfficeRelationTypeResource.PARTNER))
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
- "relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "relHolder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH" },
- "relType": "PARTNER",
- "relMark": null,
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
+ "holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH" },
+ "type": "PARTNER",
+ "mark": null,
"contact": { "label": "first contact" }
},
{
- "relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "relHolder": { "personType": "INCORPORATED_FIRM", "tradeName": "Fourth eG" },
- "relType": "PARTNER",
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
+ "holder": { "personType": "INCORPORATED_FIRM", "tradeName": "Fourth eG" },
+ "type": "PARTNER",
"contact": { "label": "fourth contact" }
},
{
- "relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "relHolder": { "personType": "LEGAL_PERSON", "tradeName": "Second e.K.", "givenName": "Peter", "familyName": "Smith" },
- "relType": "PARTNER",
- "relMark": null,
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
+ "holder": { "personType": "LEGAL_PERSON", "tradeName": "Second e.K.", "givenName": "Peter", "familyName": "Smith" },
+ "type": "PARTNER",
+ "mark": null,
"contact": { "label": "second contact" }
},
{
- "relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "relHolder": { "personType": "NATURAL_PERSON", "givenName": "Peter", "familyName": "Smith" },
- "relType": "PARTNER",
- "relMark": null,
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
+ "holder": { "personType": "NATURAL_PERSON", "givenName": "Peter", "familyName": "Smith" },
+ "type": "PARTNER",
+ "mark": null,
"contact": { "label": "sixth contact" }
},
{
- "relAnchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "relHolder": { "personType": "INCORPORATED_FIRM", "tradeName": "Third OHG" },
- "relType": "PARTNER",
- "relMark": null,
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
+ "holder": { "personType": "INCORPORATED_FIRM", "tradeName": "Third OHG" },
+ "type": "PARTNER",
+ "mark": null,
"contact": { "label": "third contact" }
}
]
@@ -119,11 +119,11 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
}
@Nested
- @Accepts({ "Relationship:C(Create)" })
- class AddRelationship {
+ @Accepts({ "Relation:C(Create)" })
+ class AddRelation {
@Test
- void globalAdmin_withoutAssumedRole_canAddRelationship() {
+ void globalAdmin_withoutAssumedRole_canAddRelation() {
context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@@ -136,38 +136,38 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON)
.body("""
{
- "relType": "%s",
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "type": "%s",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
}
""".formatted(
- HsOfficeRelationshipTypeResource.ACCOUNTING,
+ HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(),
givenHolderPerson.getUuid(),
givenContact.getUuid()))
.port(port)
.when()
- .post("http://localhost/api/hs/office/relationships")
+ .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("relType", is("ACCOUNTING"))
- .body("relAnchor.tradeName", is("Third OHG"))
- .body("relHolder.givenName", is("Paul"))
+ .body("type", is("DEBITOR"))
+ .body("anchor.tradeName", is("Third OHG"))
+ .body("holder.givenName", is("Paul"))
.body("contact.label", is("second contact"))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
- // finally, the new relationship can be accessed under the generated UUID
- final var newUserUuid = toCleanup(HsOfficeRelationshipEntity.class, UUID.fromString(
+ // finally, the new relation can be accessed under the generated UUID
+ final var newUserUuid = toCleanup(HsOfficeRelationEntity.class, UUID.fromString(
location.substring(location.lastIndexOf('/') + 1)));
assertThat(newUserUuid).isNotNull();
}
@Test
- void globalAdmin_canNotAddRelationship_ifAnchorPersonDoesNotExist() {
+ void globalAdmin_canNotAddRelation_ifAnchorPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
final var givenAnchorPersonUuid = GIVEN_NON_EXISTING_HOLDER_PERSON_UUID;
@@ -180,27 +180,27 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON)
.body("""
{
- "relType": "%s",
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "type": "%s",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
}
""".formatted(
- HsOfficeRelationshipTypeResource.ACCOUNTING,
+ HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPersonUuid,
givenHolderPerson.getUuid(),
givenContact.getUuid()))
.port(port)
.when()
- .post("http://localhost/api/hs/office/relationships")
+ .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat()
.statusCode(404)
- .body("message", is("cannot find relAnchorUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
+ .body("message", is("cannot find anchorUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
// @formatter:on
}
@Test
- void globalAdmin_canNotAddRelationship_ifHolderPersonDoesNotExist() {
+ void globalAdmin_canNotAddRelation_ifHolderPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@@ -212,27 +212,27 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON)
.body("""
{
- "relType": "%s",
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "type": "%s",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
}
""".formatted(
- HsOfficeRelationshipTypeResource.ACCOUNTING,
+ HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(),
GIVEN_NON_EXISTING_HOLDER_PERSON_UUID,
givenContact.getUuid()))
.port(port)
.when()
- .post("http://localhost/api/hs/office/relationships")
+ .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat()
.statusCode(404)
- .body("message", is("cannot find relHolderUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
+ .body("message", is("cannot find holderUuid " + GIVEN_NON_EXISTING_HOLDER_PERSON_UUID));
// @formatter:on
}
@Test
- void globalAdmin_canNotAddRelationship_ifContactDoesNotExist() {
+ void globalAdmin_canNotAddRelation_ifContactDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
@@ -245,19 +245,19 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
.contentType(ContentType.JSON)
.body("""
{
- "relType": "%s",
- "relAnchorUuid": "%s",
- "relHolderUuid": "%s",
+ "type": "%s",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
"contactUuid": "%s"
}
""".formatted(
- HsOfficeRelationshipTypeResource.ACCOUNTING,
+ HsOfficeRelationTypeResource.DEBITOR,
givenAnchorPerson.getUuid(),
givenHolderPerson.getUuid(),
givenContactUuid))
.port(port)
.when()
- .post("http://localhost/api/hs/office/relationships")
+ .post("http://localhost/api/hs/office/relations")
.then().log().all().assertThat()
.statusCode(404)
.body("message", is("cannot find contactUuid 00000000-0000-0000-0000-000000000000"));
@@ -266,97 +266,97 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
}
@Nested
- @Accepts({ "Relationship:R(Read)" })
- class GetRelationship {
+ @Accepts({ "Relation:R(Read)" })
+ class GetRelation {
@Test
- void globalAdmin_withoutAssumedRole_canGetArbitraryRelationship() {
+ void globalAdmin_withoutAssumedRole_canGetArbitraryRelation() {
context.define("superuser-alex@hostsharing.net");
- final UUID givenRelationshipUuid = findRelationship("First", "Firby").getUuid();
+ final UUID givenRelationUuid = findRelation("First", "Firby").getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
- .get("http://localhost/api/hs/office/relationships/" + givenRelationshipUuid)
+ .get("http://localhost/api/hs/office/relations/" + givenRelationUuid)
.then().log().body().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "relAnchor": { "tradeName": "First GmbH" },
- "relHolder": { "familyName": "Firby" },
+ "anchor": { "tradeName": "First GmbH" },
+ "holder": { "familyName": "Firby" },
"contact": { "label": "first contact" }
}
""")); // @formatter:on
}
@Test
- @Accepts({ "Relationship:X(Access Control)" })
- void normalUser_canNotGetUnrelatedRelationship() {
+ @Accepts({ "Relation:X(Access Control)" })
+ void normalUser_canNotGetUnrelatedRelation() {
context.define("superuser-alex@hostsharing.net");
- final UUID givenRelationshipUuid = findRelationship("First", "Firby").getUuid();
+ final UUID givenRelationUuid = findRelation("First", "Firby").getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port)
.when()
- .get("http://localhost/api/hs/office/relationships/" + givenRelationshipUuid)
+ .get("http://localhost/api/hs/office/relations/" + givenRelationUuid)
.then().log().body().assertThat()
.statusCode(404); // @formatter:on
}
@Test
- @Accepts({ "Relationship:X(Access Control)" })
- void contactAdminUser_canGetRelatedRelationship() {
+ @Accepts({ "Relation:X(Access Control)" })
+ void contactAdminUser_canGetRelatedRelation() {
context.define("superuser-alex@hostsharing.net");
- final var givenRelationship = findRelationship("First", "Firby");
- assertThat(givenRelationship.getContact().getLabel()).isEqualTo("first contact");
+ final var givenRelation = findRelation("First", "Firby");
+ assertThat(givenRelation.getContact().getLabel()).isEqualTo("first contact");
RestAssured // @formatter:off
.given()
.header("current-user", "contact-admin@firstcontact.example.com")
.port(port)
.when()
- .get("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid())
+ .get("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "relAnchor": { "tradeName": "First GmbH" },
- "relHolder": { "familyName": "Firby" },
+ "anchor": { "tradeName": "First GmbH" },
+ "holder": { "familyName": "Firby" },
"contact": { "label": "first contact" }
}
""")); // @formatter:on
}
}
- private HsOfficeRelationshipEntity findRelationship(
+ private HsOfficeRelationEntity findRelation(
final String anchorPersonName,
final String holderPersoneName) {
final var anchorPersonUuid = personRepo.findPersonByOptionalNameLike(anchorPersonName).get(0).getUuid();
final var holderPersonUuid = personRepo.findPersonByOptionalNameLike(holderPersoneName).get(0).getUuid();
- final var givenRelationship = relationshipRepo
- .findRelationshipRelatedToPersonUuid(anchorPersonUuid)
+ final var givenRelation = relationRepo
+ .findRelationRelatedToPersonUuid(anchorPersonUuid)
.stream()
- .filter(r -> r.getRelHolder().getUuid().equals(holderPersonUuid))
+ .filter(r -> r.getHolder().getUuid().equals(holderPersonUuid))
.findFirst().orElseThrow();
- return givenRelationship;
+ return givenRelation;
}
@Nested
- @Accepts({ "Relationship:U(Update)" })
- class PatchRelationship {
+ @Accepts({ "Relation:U(Update)" })
+ class PatchRelation {
@Test
- void globalAdmin_withoutAssumedRole_canPatchContactOfArbitraryRelationship() {
+ void globalAdmin_withoutAssumedRole_canPatchContactOfArbitraryRelation() {
context.define("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler();
- assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact");
+ final var givenRelation = givenSomeTemporaryRelationBessler();
+ assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off
@@ -370,109 +370,109 @@ class HsOfficeRelationshipControllerAcceptanceTest extends ContextBasedTestWithC
""".formatted(givenContact.getUuid()))
.port(port)
.when()
- .patch("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid())
+ .patch("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().all().assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("relType", is("REPRESENTATIVE"))
- .body("relAnchor.tradeName", is("Erben Bessler"))
- .body("relHolder.familyName", is("Winkler"))
+ .body("type", is("REPRESENTATIVE"))
+ .body("anchor.tradeName", is("Erben Bessler"))
+ .body("holder.familyName", is("Winkler"))
.body("contact.label", is("fourth contact"));
// @formatter:on
- // finally, the relationship is actually updated
+ // finally, the relation is actually updated
context.define("superuser-alex@hostsharing.net");
- assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isPresent().get()
+ assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isPresent().get()
.matches(rel -> {
- assertThat(rel.getRelAnchor().getTradeName()).contains("Bessler");
- assertThat(rel.getRelHolder().getFamilyName()).contains("Winkler");
+ assertThat(rel.getAnchor().getTradeName()).contains("Bessler");
+ assertThat(rel.getHolder().getFamilyName()).contains("Winkler");
assertThat(rel.getContact().getLabel()).isEqualTo("fourth contact");
- assertThat(rel.getRelType()).isEqualTo(HsOfficeRelationshipType.REPRESENTATIVE);
+ assertThat(rel.getType()).isEqualTo(HsOfficeRelationType.REPRESENTATIVE);
return true;
});
}
}
@Nested
- @Accepts({ "Relationship:D(Delete)" })
- class DeleteRelationship {
+ @Accepts({ "Relation:D(Delete)" })
+ class DeleteRelation {
@Test
- void globalAdmin_withoutAssumedRole_canDeleteArbitraryRelationship() {
+ void globalAdmin_withoutAssumedRole_canDeleteArbitraryRelation() {
context.define("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler();
+ final var givenRelation = givenSomeTemporaryRelationBessler();
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
- .delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid())
+ .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat()
.statusCode(204); // @formatter:on
- // then the given relationship is gone
- assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isEmpty();
+ // then the given relation is gone
+ assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isEmpty();
}
@Test
- @Accepts({ "Relationship:X(Access Control)" })
- void contactAdminUser_canNotDeleteRelatedRelationship() {
+ @Accepts({ "Relation:X(Access Control)" })
+ void contactAdminUser_canNotDeleteRelatedRelation() {
context.define("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler();
- assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact");
+ final var givenRelation = givenSomeTemporaryRelationBessler();
+ assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
RestAssured // @formatter:off
.given()
.header("current-user", "contact-admin@seventhcontact.example.com")
.port(port)
.when()
- .delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid())
+ .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat()
.statusCode(403); // @formatter:on
- // then the given relationship is still there
- assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isNotEmpty();
+ // then the given relation is still there
+ assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isNotEmpty();
}
@Test
- @Accepts({ "Relationship:X(Access Control)" })
- void normalUser_canNotDeleteUnrelatedRelationship() {
+ @Accepts({ "Relation:X(Access Control)" })
+ void normalUser_canNotDeleteUnrelatedRelation() {
context.define("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler();
- assertThat(givenRelationship.getContact().getLabel()).isEqualTo("seventh contact");
+ final var givenRelation = givenSomeTemporaryRelationBessler();
+ assertThat(givenRelation.getContact().getLabel()).isEqualTo("seventh contact");
RestAssured // @formatter:off
.given()
.header("current-user", "selfregistered-user-drew@hostsharing.org")
.port(port)
.when()
- .delete("http://localhost/api/hs/office/relationships/" + givenRelationship.getUuid())
+ .delete("http://localhost/api/hs/office/relations/" + givenRelation.getUuid())
.then().log().body().assertThat()
.statusCode(404); // @formatter:on
- // then the given relationship is still there
- assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isNotEmpty();
+ // then the given relation is still there
+ assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isNotEmpty();
}
}
- private HsOfficeRelationshipEntity givenSomeTemporaryRelationshipBessler() {
+ private HsOfficeRelationEntity givenSomeTemporaryRelationBessler() {
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Winkler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("seventh contact").get(0);
- final var newRelationship = HsOfficeRelationshipEntity.builder()
- .relType(HsOfficeRelationshipType.REPRESENTATIVE)
- .relAnchor(givenAnchorPerson)
- .relHolder(givenHolderPerson)
+ final var newRelation = HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.REPRESENTATIVE)
+ .anchor(givenAnchorPerson)
+ .holder(givenHolderPerson)
.contact(givenContact)
.build();
- assertThat(toCleanup(relationshipRepo.save(newRelationship))).isEqualTo(newRelationship);
+ assertThat(toCleanup(relationRepo.save(newRelation))).isEqualTo(newRelation);
- return newRelationship;
+ return newRelation;
}).assertSuccessful().returnedValue();
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcherUnitTest.java
similarity index 67%
rename from src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java
rename to src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcherUnitTest.java
index 1c12a629..6aec1b25 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityPatcherUnitTest.java
@@ -1,7 +1,7 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
-import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationshipPatchResource;
+import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach;
@@ -21,12 +21,12 @@ import static org.mockito.Mockito.lenient;
@TestInstance(PER_CLASS)
@ExtendWith(MockitoExtension.class)
-class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase<
- HsOfficeRelationshipPatchResource,
- HsOfficeRelationshipEntity
+class HsOfficeRelationEntityPatcherUnitTest extends PatchUnitTestBase<
+ HsOfficeRelationPatchResource,
+ HsOfficeRelationEntity
> {
- static final UUID INITIAL_RELATIONSHIP_UUID = UUID.randomUUID();
+ static final UUID INITIAL_RELATION_UUID = UUID.randomUUID();
static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
@Mock
@@ -49,24 +49,24 @@ class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase<
.build();
@Override
- protected HsOfficeRelationshipEntity newInitialEntity() {
- final var entity = new HsOfficeRelationshipEntity();
- entity.setUuid(INITIAL_RELATIONSHIP_UUID);
- entity.setRelType(HsOfficeRelationshipType.REPRESENTATIVE);
- entity.setRelAnchor(givenInitialAnchorPerson);
- entity.setRelHolder(givenInitialHolderPerson);
+ protected HsOfficeRelationEntity newInitialEntity() {
+ final var entity = new HsOfficeRelationEntity();
+ entity.setUuid(INITIAL_RELATION_UUID);
+ entity.setType(HsOfficeRelationType.REPRESENTATIVE);
+ entity.setAnchor(givenInitialAnchorPerson);
+ entity.setHolder(givenInitialHolderPerson);
entity.setContact(givenInitialContact);
return entity;
}
@Override
- protected HsOfficeRelationshipPatchResource newPatchResource() {
- return new HsOfficeRelationshipPatchResource();
+ protected HsOfficeRelationPatchResource newPatchResource() {
+ return new HsOfficeRelationPatchResource();
}
@Override
- protected HsOfficeRelationshipEntityPatcher createPatcher(final HsOfficeRelationshipEntity relationship) {
- return new HsOfficeRelationshipEntityPatcher(em, relationship);
+ protected HsOfficeRelationEntityPatcher createPatcher(final HsOfficeRelationEntity relation) {
+ return new HsOfficeRelationEntityPatcher(em, relation);
}
@Override
@@ -74,9 +74,9 @@ class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase<
return Stream.of(
new JsonNullableProperty<>(
"contact",
- HsOfficeRelationshipPatchResource::setContactUuid,
+ HsOfficeRelationPatchResource::setContactUuid,
PATCHED_CONTACT_UUID,
- HsOfficeRelationshipEntity::setContact,
+ HsOfficeRelationEntity::setContact,
newContact(PATCHED_CONTACT_UUID))
.notNullable()
);
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityUnitTest.java
new file mode 100644
index 00000000..bf2a7ed3
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntityUnitTest.java
@@ -0,0 +1,43 @@
+package net.hostsharing.hsadminng.hs.office.relation;
+
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HsOfficeRelationEntityUnitTest {
+
+ private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.LEGAL_PERSON)
+ .tradeName("some trade name")
+ .build();
+ private HsOfficePersonEntity holder = HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.NATURAL_PERSON)
+ .familyName("Meier")
+ .givenName("Mellie")
+ .build();
+
+ @Test
+ void toStringReturnsAllProperties() {
+ final var given = HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.SUBSCRIBER)
+ .mark("members-announce")
+ .anchor(anchor)
+ .holder(holder)
+ .build();
+
+ assertThat(given.toString()).isEqualTo("rel(anchor='LP some trade name', type='SUBSCRIBER', mark='members-announce', holder='NP Meier, Mellie')");
+ }
+
+ @Test
+ void toShortString() {
+ final var given = HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.REPRESENTATIVE)
+ .anchor(anchor)
+ .holder(holder)
+ .build();
+
+ assertThat(given.toShortString()).isEqualTo("rel(anchor='LP some trade name', type='REPRESENTATIVE', holder='NP Meier, Mellie')");
+ }
+}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
similarity index 54%
rename from src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java
rename to src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
index 46d60a40..545e7b03 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
@@ -1,4 +1,4 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
+package net.hostsharing.hsadminng.hs.office.relation;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
@@ -29,10 +29,10 @@ import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Import( { Context.class, JpaAttempt.class })
-class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
+class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired
- HsOfficeRelationshipRepository relationshipRepo;
+ HsOfficeRelationRepository relationRepo;
@Autowired
HsOfficePersonRepository personRepo;
@@ -56,33 +56,33 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
HttpServletRequest request;
@Nested
- class CreateRelationship {
+ class CreateRelation {
@Test
- public void testHostsharingAdmin_withoutAssumedRole_canCreateNewRelationship() {
+ public void testHostsharingAdmin_withoutAssumedRole_canCreateNewRelation() {
// given
context("superuser-alex@hostsharing.net");
- final var count = relationshipRepo.count();
+ final var count = relationRepo.count();
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
// when
final var result = attempt(em, () -> {
- final var newRelationship = HsOfficeRelationshipEntity.builder()
- .relAnchor(givenAnchorPerson)
- .relHolder(givenHolderPerson)
- .relType(HsOfficeRelationshipType.REPRESENTATIVE)
+ final var newRelation = HsOfficeRelationEntity.builder()
+ .anchor(givenAnchorPerson)
+ .holder(givenHolderPerson)
+ .type(HsOfficeRelationType.REPRESENTATIVE)
.contact(givenContact)
.build();
- return toCleanup(relationshipRepo.save(newRelationship));
+ return toCleanup(relationRepo.save(newRelation));
});
// then
result.assertSuccessful();
- assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationshipEntity::getUuid).isNotNull();
- assertThatRelationshipIsPersisted(result.returnedValue());
- assertThat(relationshipRepo.count()).isEqualTo(count + 1);
+ assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationEntity::getUuid).isNotNull();
+ assertThatRelationIsPersisted(result.returnedValue());
+ assertThat(relationRepo.count()).isEqualTo(count + 1);
}
@Test
@@ -97,190 +97,190 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
- final var newRelationship = HsOfficeRelationshipEntity.builder()
- .relAnchor(givenAnchorPerson)
- .relHolder(givenHolderPerson)
- .relType(HsOfficeRelationshipType.REPRESENTATIVE)
+ final var newRelation = HsOfficeRelationEntity.builder()
+ .anchor(givenAnchorPerson)
+ .holder(givenHolderPerson)
+ .type(HsOfficeRelationType.REPRESENTATIVE)
.contact(givenContact)
.build();
- return toCleanup(relationshipRepo.save(newRelationship));
+ return toCleanup(relationRepo.save(newRelation));
});
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin",
- "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner",
- "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant"));
+ "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin",
+ "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner",
+ "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm DELETE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }",
+ "{ grant perm DELETE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }",
- "{ grant perm UPDATE on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
+ "{ grant perm UPDATE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
- "{ grant perm SELECT on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
+ "{ grant perm SELECT on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
- "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
- "{ grant role hs_office_contact#fourthcontact.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
- "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
+ "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
+ "{ grant role hs_office_contact#fourthcontact.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
+ "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
null)
);
}
- private void assertThatRelationshipIsPersisted(final HsOfficeRelationshipEntity saved) {
- final var found = relationshipRepo.findByUuid(saved.getUuid());
+ private void assertThatRelationIsPersisted(final HsOfficeRelationEntity saved) {
+ final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
}
}
@Nested
- class FindAllRelationships {
+ class FindAllRelations {
@Test
- public void globalAdmin_withoutAssumedRole_canViewAllRelationshipsOfArbitraryPerson() {
+ public void globalAdmin_withoutAssumedRole_canViewAllRelationsOfArbitraryPerson() {
// given
context("superuser-alex@hostsharing.net");
final var person = personRepo.findPersonByOptionalNameLike("Second e.K.").stream().findFirst().orElseThrow();
// when
- final var result = relationshipRepo.findRelationshipRelatedToPersonUuid(person.getUuid());
+ final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
// then
- allTheseRelationshipsAreReturned(
+ allTheseRelationsAreReturned(
result,
- "rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP Second e.K.', contact='second contact')",
- "rel(relAnchor='LP Second e.K.', relType='REPRESENTATIVE', relHolder='NP Smith, Peter', contact='second contact')");
+ "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP Second e.K.', contact='second contact')",
+ "rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')");
}
@Test
- public void normalUser_canViewRelationshipsOfOwnedPersons() {
+ public void normalUser_canViewRelationsOfOwnedPersons() {
// given:
context("person-FirstGmbH@example.com");
final var person = personRepo.findPersonByOptionalNameLike("First").stream().findFirst().orElseThrow();
// when:
- final var result = relationshipRepo.findRelationshipRelatedToPersonUuid(person.getUuid());
+ final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
// then:
- exactlyTheseRelationshipsAreReturned(
+ exactlyTheseRelationsAreReturned(
result,
- "rel(relAnchor='LP Hostsharing eG', relType='PARTNER', relHolder='LP First GmbH', contact='first contact')",
- "rel(relAnchor='LP First GmbH', relType='REPRESENTATIVE', relHolder='NP Firby, Susan', contact='first contact')");
+ "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP First GmbH', contact='first contact')",
+ "rel(anchor='LP First GmbH', type='REPRESENTATIVE', holder='NP Firby, Susan', contact='first contact')");
}
}
@Nested
- class UpdateRelationship {
+ class UpdateRelation {
@Test
- public void hostsharingAdmin_withoutAssumedRole_canUpdateContactOfArbitraryRelationship() {
+ public void hostsharingAdmin_withoutAssumedRole_canUpdateContactOfArbitraryRelation() {
// given
context("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "fifth contact");
- assertThatRelationshipIsVisibleForUserWithRole(
- givenRelationship,
+ assertThatRelationIsVisibleForUserWithRole(
+ givenRelation,
"hs_office_person#ErbenBesslerMelBessler.admin");
- assertThatRelationshipActuallyInDatabase(givenRelationship);
+ assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net");
final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- givenRelationship.setContact(givenContact);
- return toCleanup(relationshipRepo.save(givenRelationship));
+ givenRelation.setContact(givenContact);
+ return toCleanup(relationRepo.save(givenRelation));
});
// then
result.assertSuccessful();
assertThat(result.returnedValue().getContact().getLabel()).isEqualTo("sixth contact");
- assertThatRelationshipIsVisibleForUserWithRole(
+ assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
"global#global.admin");
- assertThatRelationshipIsVisibleForUserWithRole(
+ assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_contact#sixthcontact.admin");
- assertThatRelationshipIsNotVisibleForUserWithRole(
+ assertThatRelationIsNotVisibleForUserWithRole(
result.returnedValue(),
"hs_office_contact#fifthcontact.admin");
- relationshipRepo.deleteByUuid(givenRelationship.getUuid());
+ relationRepo.deleteByUuid(givenRelation.getUuid());
}
@Test
- public void relHolderAdmin_canNotUpdateRelatedRelationship() {
+ public void holderAdmin_canNotUpdateRelatedRelation() {
// given
context("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "eighth");
- assertThatRelationshipIsVisibleForUserWithRole(
- givenRelationship,
+ assertThatRelationIsVisibleForUserWithRole(
+ givenRelation,
"hs_office_person#BesslerAnita.admin");
- assertThatRelationshipActuallyInDatabase(givenRelationship);
+ assertThatRelationActuallyInDatabase(givenRelation);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita.admin");
- givenRelationship.setContact(null);
- return relationshipRepo.save(givenRelationship);
+ givenRelation.setContact(null);
+ return relationRepo.save(givenRelation);
});
// then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] Subject ", " is not allowed to update hs_office_relationship uuid");
+ "[403] Subject ", " is not allowed to update hs_office_relation uuid");
}
@Test
- public void contactAdmin_canNotUpdateRelatedRelationship() {
+ public void contactAdmin_canNotUpdateRelatedRelation() {
// given
context("superuser-alex@hostsharing.net");
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "ninth");
- assertThatRelationshipIsVisibleForUserWithRole(
- givenRelationship,
+ assertThatRelationIsVisibleForUserWithRole(
+ givenRelation,
"hs_office_contact#ninthcontact.admin");
- assertThatRelationshipActuallyInDatabase(givenRelationship);
+ assertThatRelationActuallyInDatabase(givenRelation);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin");
- givenRelationship.setContact(null); // TODO
- return relationshipRepo.save(givenRelationship);
+ givenRelation.setContact(null); // TODO
+ return relationRepo.save(givenRelation);
});
// then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] Subject ", " is not allowed to update hs_office_relationship uuid");
+ "[403] Subject ", " is not allowed to update hs_office_relation uuid");
}
- private void assertThatRelationshipActuallyInDatabase(final HsOfficeRelationshipEntity saved) {
- final var found = relationshipRepo.findByUuid(saved.getUuid());
+ private void assertThatRelationActuallyInDatabase(final HsOfficeRelationEntity saved) {
+ final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved);
}
- private void assertThatRelationshipIsVisibleForUserWithRole(
- final HsOfficeRelationshipEntity entity,
+ private void assertThatRelationIsVisibleForUserWithRole(
+ final HsOfficeRelationEntity entity,
final String assumedRoles) {
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles);
- assertThatRelationshipActuallyInDatabase(entity);
+ assertThatRelationActuallyInDatabase(entity);
}).assertSuccessful();
}
- private void assertThatRelationshipIsNotVisibleForUserWithRole(
- final HsOfficeRelationshipEntity entity,
+ private void assertThatRelationIsNotVisibleForUserWithRole(
+ final HsOfficeRelationEntity entity,
final String assumedRoles) {
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles);
- final var found = relationshipRepo.findByUuid(entity.getUuid());
+ final var found = relationRepo.findByUuid(entity.getUuid());
assertThat(found).isEmpty();
}).assertSuccessful();
}
@@ -290,63 +290,63 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
class DeleteByUuid {
@Test
- public void globalAdmin_withoutAssumedRole_canDeleteAnyRelationship() {
+ public void globalAdmin_withoutAssumedRole_canDeleteAnyRelation() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "tenth");
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- relationshipRepo.deleteByUuid(givenRelationship.getUuid());
+ relationRepo.deleteByUuid(givenRelation.getUuid());
});
// then
result.assertSuccessful();
assertThat(jpaAttempt.transacted(() -> {
context("superuser-fran@hostsharing.net", null);
- return relationshipRepo.findByUuid(givenRelationship.getUuid());
+ return relationRepo.findByUuid(givenRelation.getUuid());
}).assertSuccessful().returnedValue()).isEmpty();
}
@Test
- public void contactUser_canViewButNotDeleteTheirRelatedRelationship() {
+ public void contactUser_canViewButNotDeleteTheirRelatedRelation() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "eleventh");
// when
final var result = jpaAttempt.transacted(() -> {
context("contact-admin@eleventhcontact.example.com");
- assertThat(relationshipRepo.findByUuid(givenRelationship.getUuid())).isPresent();
- relationshipRepo.deleteByUuid(givenRelationship.getUuid());
+ assertThat(relationRepo.findByUuid(givenRelation.getUuid())).isPresent();
+ relationRepo.deleteByUuid(givenRelation.getUuid());
});
// then
result.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
- "[403] Subject ", " not allowed to delete hs_office_relationship");
+ "[403] Subject ", " not allowed to delete hs_office_relation");
assertThat(jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- return relationshipRepo.findByUuid(givenRelationship.getUuid());
+ return relationRepo.findByUuid(givenRelation.getUuid());
}).assertSuccessful().returnedValue()).isPresent(); // still there
}
@Test
- public void deletingARelationshipAlsoDeletesRelatedRolesAndGrants() {
+ public void deletingARelationAlsoDeletesRelatedRolesAndGrants() {
// given
context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
- final var givenRelationship = givenSomeTemporaryRelationshipBessler(
+ final var givenRelation = givenSomeTemporaryRelationBessler(
"Anita", "twelfth");
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- return relationshipRepo.deleteByUuid(givenRelationship.getUuid());
+ return relationRepo.deleteByUuid(givenRelation.getUuid());
});
// then
@@ -363,7 +363,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
final var query = em.createNativeQuery("""
select currentTask, targetTable, targetOp
from tx_journal_v
- where targettable = 'hs_office_relationship';
+ where targettable = 'hs_office_relation';
""");
// when
@@ -371,40 +371,40 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
- "[creating relationship test-data HostsharingeG-FirstGmbH, hs_office_relationship, INSERT]",
- "[creating relationship test-data FirstGmbH-Firby, hs_office_relationship, INSERT]");
+ "[creating relation test-data HostsharingeG-FirstGmbH, hs_office_relation, INSERT]",
+ "[creating relation test-data FirstGmbH-Firby, hs_office_relation, INSERT]");
}
- private HsOfficeRelationshipEntity givenSomeTemporaryRelationshipBessler(final String holderPerson, final String contact) {
+ private HsOfficeRelationEntity givenSomeTemporaryRelationBessler(final String holderPerson, final String contact) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenHolderPerson = personRepo.findPersonByOptionalNameLike(holderPerson).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
- final var newRelationship = HsOfficeRelationshipEntity.builder()
- .relType(HsOfficeRelationshipType.REPRESENTATIVE)
- .relAnchor(givenAnchorPerson)
- .relHolder(givenHolderPerson)
+ final var newRelation = HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.REPRESENTATIVE)
+ .anchor(givenAnchorPerson)
+ .holder(givenHolderPerson)
.contact(givenContact)
.build();
- return toCleanup(relationshipRepo.save(newRelationship));
+ return toCleanup(relationRepo.save(newRelation));
}).assertSuccessful().returnedValue();
}
- void exactlyTheseRelationshipsAreReturned(
- final List actualResult,
- final String... relationshipNames) {
+ void exactlyTheseRelationsAreReturned(
+ final List actualResult,
+ final String... relationNames) {
assertThat(actualResult)
- .extracting(HsOfficeRelationshipEntity::toString)
- .containsExactlyInAnyOrder(relationshipNames);
+ .extracting(HsOfficeRelationEntity::toString)
+ .containsExactlyInAnyOrder(relationNames);
}
- void allTheseRelationshipsAreReturned(
- final List actualResult,
- final String... relationshipNames) {
+ void allTheseRelationsAreReturned(
+ final List actualResult,
+ final String... relationNames) {
assertThat(actualResult)
- .extracting(HsOfficeRelationshipEntity::toString)
- .contains(relationshipNames);
+ .extracting(HsOfficeRelationEntity::toString)
+ .contains(relationNames);
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java
deleted file mode 100644
index 59433fa2..00000000
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package net.hostsharing.hsadminng.hs.office.relationship;
-
-import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-
-class HsOfficeRelationshipEntityUnitTest {
-
- private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder()
- .personType(HsOfficePersonType.LEGAL_PERSON)
- .tradeName("some trade name")
- .build();
- private HsOfficePersonEntity holder = HsOfficePersonEntity.builder()
- .personType(HsOfficePersonType.NATURAL_PERSON)
- .familyName("Meier")
- .givenName("Mellie")
- .build();
-
- @Test
- void toStringReturnsAllProperties() {
- final var given = HsOfficeRelationshipEntity.builder()
- .relType(HsOfficeRelationshipType.SUBSCRIBER)
- .relMark("members-announce")
- .relAnchor(anchor)
- .relHolder(holder)
- .build();
-
- assertThat(given.toString()).isEqualTo("rel(relAnchor='LP some trade name', relType='SUBSCRIBER', relMark='members-announce', relHolder='NP Meier, Mellie')");
- }
-
- @Test
- void toShortString() {
- final var given = HsOfficeRelationshipEntity.builder()
- .relType(HsOfficeRelationshipType.REPRESENTATIVE)
- .relAnchor(anchor)
- .relHolder(holder)
- .build();
-
- assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LP some trade name', relType='REPRESENTATIVE', relHolder='NP Meier, Mellie')");
- }
-}
diff --git a/tools/generate b/tools/generate
deleted file mode 100755
index 93aa5c7c..00000000
--- a/tools/generate
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-
-sourceLower=partner
-targetLower=relationship
-
-sourceStudly=Partner
-targetStudly=Relationship
-
-## for source in `find src -iname ""*$sourceLower*"" -type f \( -iname \*.yaml -o -iname \*.sql -o -iname \*.java \)`; do
-for source in `find src -iname ""*$sourceLower*"" -type f \( -iname \*.yaml \)`; do
- target=`echo $source | sed -e "s/$sourceStudly/$targetStudly/g" -e "s/$sourceLower/$targetLower/g"`
- echo "Generating $target from $source:"
-
- mkdir -p `dirname $target`
-
- sed -e 's/hs-office-partner/hs-office-relationship/g' \
- -e 's/hs_office_partner/hs_office_relationship/g' \
- -e 's/HsOfficePartner/HsOfficeRelationship/g' \
- -e 's/hsOfficePartner/hsOfficeRelationship/g' \
- -e 's/partner/relationship/g' \
- \
- -e 's/addPartner/addRelationship/g' \
- -e 's/listPartners/listRelationships/g' \
- -e 's/getPartnerByUuid/getRelationshipByUuid/g' \
- -e 's/patchPartner/patchRelationship/g' \
- -e 's/person/relHolder/g' \
- -e 's/registrationOffice/relType/g' \
- <$source >$target
-
-done
-
-exit
-
-cat >>src/main/resources/db/changelog/db.changelog-master.yaml <
Date: Thu, 14 Mar 2024 12:52:24 +0100
Subject: [PATCH 04/12] fix setting relation mark via API (#24)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/24
Reviewed-by: Timotheus Pokorra
---
.../hs/office/relation/HsOfficeRelationController.java | 1 +
.../hs-office/hs-office-relations-schemas.yaml | 3 ++-
.../relation/HsOfficeRelationControllerAcceptanceTest.java | 7 +++++--
.../HsOfficeRelationRepositoryIntegrationTest.java | 6 +++++-
4 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
index a7923128..e1f80148 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationController.java
@@ -70,6 +70,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
final var entityToSave = new HsOfficeRelationEntity();
entityToSave.setType(HsOfficeRelationType.valueOf(body.getType()));
+ entityToSave.setMark(body.getMark());
entityToSave.setAnchor(holderRepo.findByUuid(body.getAnchorUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find anchorUuid " + body.getAnchorUuid())
));
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
index b092cd0a..7b316b40 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
@@ -55,6 +55,7 @@ components:
nullable: true
mark:
type: string
+ nullable: true
contactUuid:
type: string
format: uuid
@@ -62,4 +63,4 @@ components:
- anchorUuid
- holderUuid
- type
- - relContactUuid
+ - contactUuid
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
index fd978e1d..c4654bd3 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
@@ -137,12 +137,14 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
.body("""
{
"type": "%s",
+ "mark": "%s",
"anchorUuid": "%s",
"holderUuid": "%s",
"contactUuid": "%s"
}
""".formatted(
- HsOfficeRelationTypeResource.DEBITOR,
+ HsOfficeRelationTypeResource.SUBSCRIBER,
+ "operations-discuss",
givenAnchorPerson.getUuid(),
givenHolderPerson.getUuid(),
givenContact.getUuid()))
@@ -153,7 +155,8 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("type", is("DEBITOR"))
+ .body("type", is("SUBSCRIBER"))
+ .body("mark", is("operations-discuss"))
.body("anchor.tradeName", is("Third OHG"))
.body("holder.givenName", is("Paul"))
.body("contact.label", is("second contact"))
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
index 545e7b03..5c10af88 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
@@ -72,7 +72,8 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
final var newRelation = HsOfficeRelationEntity.builder()
.anchor(givenAnchorPerson)
.holder(givenHolderPerson)
- .type(HsOfficeRelationType.REPRESENTATIVE)
+ .type(HsOfficeRelationType.SUBSCRIBER)
+ .mark("operations-announce")
.contact(givenContact)
.build();
return toCleanup(relationRepo.save(newRelation));
@@ -83,6 +84,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeRelationEntity::getUuid).isNotNull();
assertThatRelationIsPersisted(result.returnedValue());
assertThat(relationRepo.count()).isEqualTo(count + 1);
+ final var stored = relationRepo.findByUuid(result.returnedValue().getUuid());
+ assertThat(stored).isNotEmpty().map(HsOfficeRelationEntity::toString).get()
+ .isEqualTo("rel(anchor='NP Bessler, Anita', type='SUBSCRIBER', mark='operations-announce', holder='NP Bessler, Anita', contact='fourth contact')");
}
@Test
From 4572c6bda0e8bf6fa73ef99cf154d53bbedf60a8 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 26 Mar 2024 11:25:18 +0100
Subject: [PATCH 05/12] improved RBAC generators (#26)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/26
Reviewed-by: Timotheus Pokorra
---
.../office/debitor/HsOfficeDebitorEntity.java | 29 +-
.../partner/HsOfficePartnerDetailsEntity.java | 4 +-
.../office/partner/HsOfficePartnerEntity.java | 8 +-
.../relation/HsOfficeRelationEntity.java | 15 +-
.../rbac/rbacdef/InsertTriggerGenerator.java | 161 ++++++--
.../rbacdef/RbacIdentityViewGenerator.java | 12 +-
.../rbacdef/RbacRestrictedViewGenerator.java | 12 +-
.../hsadminng/rbac/rbacdef/RbacView.java | 378 +++++++++++++++---
.../RbacViewMermaidFlowchartGenerator.java | 4 +-
.../rbacdef/RbacViewPostgresGenerator.java | 11 +-
.../RolesGrantsAndPermissionsGenerator.java | 108 ++++-
.../hsadminng/rbac/rbacdef/StringWriter.java | 38 +-
.../rbac/rbacgrant/RbacGrantController.java | 13 +
.../rbacgrant/RbacGrantsDiagramService.java | 42 +-
.../hsadminng/rbac/rbacrole/RbacRoleType.java | 2 +-
.../test/cust/TestCustomerEntity.java | 3 +-
.../hsadminng/test/dom/TestDomainEntity.java | 11 +-
.../hsadminng/test/pac/TestPackageEntity.java | 9 +-
.../db/changelog/007-table-columns.sql | 20 +
.../resources/db/changelog/010-context.sql | 1 +
.../resources/db/changelog/050-rbac-base.sql | 97 ++++-
.../db/changelog/054-rbac-context.sql | 7 +-
.../db/changelog/057-rbac-role-builder.sql | 21 +-
.../db/changelog/058-rbac-generators.sql | 42 +-
.../db/changelog/080-rbac-global.sql | 27 +-
.../db/changelog/113-test-customer-rbac.md | 4 +-
.../db/changelog/113-test-customer-rbac.sql | 68 +++-
.../db/changelog/123-test-package-rbac.md | 2 +-
.../db/changelog/123-test-package-rbac.sql | 56 +--
.../db/changelog/133-test-domain-rbac.md | 2 +-
.../db/changelog/133-test-domain-rbac.sql | 56 +--
.../203-hs-office-contact-rbac-generated.md | 43 ++
.../203-hs-office-contact-rbac-generated.sql | 126 ++++++
.../213-hs-office-person-rbac-generated.md | 43 ++
.../213-hs-office-person-rbac-generated.sql | 126 ++++++
.../223-hs-office-relation-rbac-generated.md | 100 +++++
.../223-hs-office-relation-rbac-generated.sql | 191 +++++++++
.../233-hs-office-partner-rbac-generated.md | 158 ++++++++
.../233-hs-office-partner-rbac-generated.sql | 248 ++++++++++++
...s-office-partner-details-rbac-generated.md | 136 +++++++
...-office-partner-details-rbac-generated.sql | 164 ++++++++
...43-hs-office-bankaccount-rbac-generated.md | 43 ++
...3-hs-office-bankaccount-rbac-generated.sql | 125 ++++++
...53-hs-office-sepamandate-rbac-generated.md | 178 +++++++++
...3-hs-office-sepamandate-rbac-generated.sql | 143 +++++++
.../273-hs-office-debitor-rbac-generated.md | 275 +++++++++++++
.../273-hs-office-debitor-rbac-generated.sql | 231 +++++++++++
.../db/changelog/db.changelog-master.yaml | 2 +
...iceMembershipControllerAcceptanceTest.java | 2 +-
.../TestCustomerControllerAcceptanceTest.java | 2 +-
.../test/cust/TestCustomerEntityUnitTest.java | 2 +
src/test/resources/application.yml | 3 +-
52 files changed, 3295 insertions(+), 309 deletions(-)
create mode 100644 src/main/resources/db/changelog/007-table-columns.sql
create mode 100644 src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
create mode 100644 src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 66f82f95..4fb08538 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -19,9 +19,10 @@ import java.util.Optional;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -131,36 +132,26 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"vatBusiness",
"vatReverseCharge",
"defaultPrefix" /* TODO: do we want that updatable? */)
- .createPermission(custom("new-debitor")).grantedTo("global", ADMIN)
+ .createPermission(INSERT).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
- fetchedBySql("""
- SELECT *
- FROM hs_office_relation AS r
- WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
- """),
+ directlyFetchedByDependsOnColumn(),
dependsOnColumn("debitorRelUuid"))
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
- dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
- SELECT *
- FROM hs_office_relation AS r
- WHERE r.type = 'DEBITOR' AND r.holderUuid = ${REF}.debitorRelUuid
- """)
- )
+ dependsOnColumn("refundBankAccountUuid"),
+ directlyFetchedByDependsOnColumn(),
+ NULLABLE)
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
- dependsOnColumn("partnerRelUuid"), fetchedBySql("""
- SELECT *
- FROM hs_office_relation AS partnerRel
- WHERE ${debitorRel}.anchorUuid = partnerRel.holderUuid
- """)
- )
+ dependsOnColumn("partnerRelUuid"),
+ directlyFetchedByDependsOnColumn(),
+ NULLABLE)
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index 435357fe..acf39249 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -84,11 +84,11 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
"birthName",
"birthday",
"dateOfDeath")
- .createPermission(custom("new-partner-details")).grantedTo("global", ADMIN)
+ .createPermission(INSERT).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
fetchedBySql("""
- SELECT partnerRel.*
+ SELECT ${columns}
FROM hs_office_relation AS partnerRel
JOIN hs_office_partner AS partner
ON partner.detailsUuid = ${ref}.uuid
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
index 8e35e9b0..b16dcc76 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
@@ -22,7 +22,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnCo
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -90,17 +90,17 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
"partnerRelUuid",
"personUuid",
"contactUuid")
- .createPermission(custom("new-partner")).grantedTo("global", ADMIN)
+ .createPermission(INSERT).grantedTo("global", ADMIN)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
- fetchedBySql("SELECT * FROM hs_office_relation AS r WHERE r.uuid = ${ref}.partnerRelUuid"),
+ directlyFetchedByDependsOnColumn(),
dependsOnColumn("partnerRelUuid"))
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
- fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
+ directlyFetchedByDependsOnColumn(),
dependsOnColumn("detailsUuid"))
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
index 71e2b11a..364368af 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
@@ -16,10 +16,11 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -90,16 +91,16 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
.withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("anchorUuid"),
- fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.anchorUuid")
- )
+ directlyFetchedByDependsOnColumn(),
+ NULLABLE)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("holderUuid"),
- fetchedBySql("select * from hs_office_person as p where p.uuid = ${REF}.holderUuid")
- )
+ directlyFetchedByDependsOnColumn(),
+ NULLABLE)
.importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"),
- fetchedBySql("select * from hs_office_contact as c where c.uuid = ${REF}.contactUuid")
- )
+ directlyFetchedByDependsOnColumn(),
+ NULLABLE)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
index 5303c27e..2e0a4a2f 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
@@ -4,6 +4,7 @@ import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
+import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
@@ -22,7 +23,7 @@ public class InsertTriggerGenerator {
void generateTo(final StringWriter plPgSql) {
generateLiquibaseChangesetHeader(plPgSql);
- generateGrantInsertRoleToExistingCustomers(plPgSql);
+ generateGrantInsertRoleToExistingObjects(plPgSql);
generateInsertPermissionGrantTrigger(plPgSql);
generateInsertCheckTrigger(plPgSql);
plPgSql.writeLn("--//");
@@ -37,7 +38,7 @@ public class InsertTriggerGenerator {
with("liquibaseTagPrefix", liquibaseTagPrefix));
}
- private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
+ private void generateGrantInsertRoleToExistingObjects(final StringWriter plPgSql) {
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
plPgSql.writeLn("""
/*
@@ -53,16 +54,16 @@ public class InsertTriggerGenerator {
FOR row IN SELECT * FROM ${rawSuperTableName}
LOOP
- roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
+ roleUuid := findRoleId(${rawSuperRoleDescriptor});
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
- call grantPermissionToRole(roleUuid, permissionUuid);
+ call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
- with("rawSuperRoleDescriptor", toVar(superRoleDef))
+ with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
);
});
}
@@ -79,39 +80,69 @@ public class InsertTriggerGenerator {
strict as $$
begin
call grantPermissionToRole(
- ${rawSuperRoleDescriptor}(NEW),
- createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
+ createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'),
+ ${rawSuperRoleDescriptor});
return NEW;
end; $$;
- create trigger ${rawSubTableName}_${rawSuperTableName}_insert_tg
+ -- z_... is to put it at the end of after insert triggers, to make sure the roles exist
+ create trigger z_${rawSubTableName}_${rawSuperTableName}_insert_tg
after insert on ${rawSuperTableName}
for each row
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
""",
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
- with("rawSuperRoleDescriptor", toVar(superRoleDef))
+ with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, NEW.name()))
);
});
}
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
- plPgSql.writeLn("""
- /**
- Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
- */
- create or replace function ${rawSubTable}_insert_permission_missing_tf()
- returns trigger
- language plpgsql as $$
- begin
- raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
- currentSubjects(), currentSubjectsUuids();
- end; $$;
- """,
- with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
getOptionalInsertGrant().ifPresentOrElse(g -> {
- plPgSql.writeLn("""
+ if (g.getSuperRoleDef().getEntityAlias().isGlobal()) {
+ switch (g.getSuperRoleDef().getRole()) {
+ case ADMIN -> {
+ generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
+ }
+ case GUEST -> {
+ // no permission check trigger generated, as anybody can insert rows into this table
+ }
+ default -> {
+ throw new IllegalArgumentException(
+ "invalid global role for INSERT permission: " + g.getSuperRoleDef().getRole());
+ }
+ }
+ } else {
+ if (g.getSuperRoleDef().getEntityAlias().isFetchedByDirectForeignKey()) {
+ generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(plPgSql, g);
+ } else {
+ generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(plPgSql, g);
+ }
+ }
+ },
+ () -> {
+ System.err.println("WARNING: no explicit INSERT grant for " + rbacDef.getRootEntityAlias().simpleName() + " => implicitly grant INSERT to global.admin");
+ generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
+ });
+ }
+
+ private void generateInsertPermissionTriggerAllowByRoleOfDirectForeignKey(final StringWriter plPgSql, final RbacView.RbacGrantDefinition g) {
+ plPgSql.writeLn("""
+ /**
+ Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
+ 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 ${rawSubTable}_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+ begin
+ raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+ end; $$;
+
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
@@ -119,20 +150,78 @@ public class InsertTriggerGenerator {
execute procedure ${rawSubTable}_insert_permission_missing_tf();
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
- with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
- },
- () -> {
- plPgSql.writeLn("""
+ with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName()));
+ }
+
+ private void generateInsertPermissionTriggerAllowByRoleOfIndirectForeignKey(
+ final StringWriter plPgSql,
+ final RbacView.RbacGrantDefinition g) {
+ plPgSql.writeLn("""
+ /**
+ Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
+ 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 ${rawSubTable}_insert_permission_check_tf()
+ returns trigger
+ language plpgsql as $$
+
+ declare
+ superRoleObjectUuid uuid;
+
+ begin
+ """,
+ with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
+ plPgSql.chopEmptyLines();
+ plPgSql.indented(2, () -> {
+ plPgSql.writeLn(
+ "superRoleObjectUuid := (" + g.getSuperRoleDef().getEntityAlias().fetchSql().sql + ");\n" +
+ "assert superRoleObjectUuid is not null, 'superRoleObjectUuid must not be null';",
+ with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"),
+ with("ref", NEW.name()));
+ });
+ plPgSql.writeLn();
+ plPgSql.writeLn("""
+ if ( not hasInsertPermission(superRoleObjectUuid, 'INSERT', '${rawSubTable}') ) then
+ raise exception
+ '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+ end if;
+ return NEW;
+ end; $$;
+
create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable}
for each row
- -- As there is no explicit INSERT grant specified for this table,
- -- only global admins are allowed to insert any rows.
- when ( not isGlobalAdmin() )
- execute procedure ${rawSubTable}_insert_permission_missing_tf();
+ execute procedure ${rawSubTable}_insert_permission_check_tf();
+
""",
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
- });
+ }
+
+ private void generateInsertPermissionTriggerAllowOnlyGlobalAdmin(final StringWriter plPgSql) {
+ plPgSql.writeLn("""
+ /**
+ Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable},
+ where only global-admin has that permission.
+ */
+ create or replace function ${rawSubTable}_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+ begin
+ raise exception '[403] insert into ${rawSubTable} not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+ end; $$;
+
+ create trigger ${rawSubTable}_insert_permission_check_tg
+ before insert on ${rawSubTable}
+ for each row
+ when ( not isGlobalAdmin() )
+ execute procedure ${rawSubTable}_insert_permission_missing_tf();
+ """,
+ with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
}
private Stream getInsertGrants() {
@@ -162,4 +251,12 @@ public class InsertTriggerGenerator {
return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
}
+
+ private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
+ final var functionName = toVar(roleDef);
+ if (roleDef.getEntityAlias().isGlobal()) {
+ return functionName + "()";
+ }
+ return functionName + "(" + ref + ")";
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
index d664a83b..066acba2 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacIdentityViewGenerator.java
@@ -26,18 +26,20 @@ public class RbacIdentityViewGenerator {
plPgSql.writeLn(
switch (rbacDef.getIdentityViewSqlQuery().part) {
case SQL_PROJECTION -> """
- call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
- ${identityViewSqlPart}
+ call generateRbacIdentityViewFromProjection('${rawTableName}',
+ $idName$
+ ${identityViewSqlPart}
$idName$);
""";
case SQL_QUERY -> """
- call generateRbacIdentityViewFromProjection('${rawTableName}', $idName$
- ${identityViewSqlPart}
+ call generateRbacIdentityViewFromQuery('${rawTableName}',
+ $idName$
+ ${identityViewSqlPart}
$idName$);
""";
default -> throw new IllegalStateException("illegal SQL part given");
},
- with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
+ with("identityViewSqlPart", StringWriter.indented(2, rbacDef.getIdentityViewSqlQuery().sql)),
with("rawTableName", rawTableName));
plPgSql.writeLn("--//");
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
index f8f6e890..b5757865 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacRestrictedViewGenerator.java
@@ -8,13 +8,11 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacRestrictedViewGenerator {
private final RbacView rbacDef;
private final String liquibaseTagPrefix;
- private final String simpleEntityVarName;
private final String rawTableName;
public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef;
this.liquibaseTagPrefix = liquibaseTagPrefix;
- this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
}
@@ -24,7 +22,9 @@ public class RbacRestrictedViewGenerator {
--changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('${rawTableName}',
- '${orderBy}',
+ $orderBy$
+ ${orderBy}
+ $orderBy$,
$updates$
${updates}
$updates$);
@@ -32,10 +32,10 @@ public class RbacRestrictedViewGenerator {
""",
with("liquibaseTagPrefix", liquibaseTagPrefix),
- with("orderBy", rbacDef.getOrderBySqlExpression().sql),
- with("updates", indented(rbacDef.getUpdatableColumns().stream()
+ with("orderBy", indented(2, rbacDef.getOrderBySqlExpression().sql)),
+ with("updates", indented(2, rbacDef.getUpdatableColumns().stream()
.map(c -> c + " = new." + c)
- .collect(joining(",\n")), 2)),
+ .collect(joining(",\n")))),
with("rawTableName", rawTableName));
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
index 2d5cd93c..d6fe2ab3 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
@@ -32,8 +32,10 @@ import java.util.stream.Stream;
import static java.lang.reflect.Modifier.isStatic;
import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.autoFetched;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static org.apache.commons.lang3.StringUtils.uncapitalize;
@Getter
@@ -65,6 +67,21 @@ public class RbacView {
private EntityAlias rootEntityAliasProxy;
private RbacRoleDefinition previousRoleDef;
+ /** Crates an RBAC definition template for the given entity class and defining the given alias.
+ *
+ * @param alias
+ * an alias name for this entity/table, which can be used in further grants
+ *
+ * @param entityClass
+ * the Java class for which this RBAC definition is to be defined
+ * (the class to which the calling method belongs)
+ *
+ * @return
+ * the newly created RBAC definition template
+ *
+ * @param
+ * a JPA entity class extending RbacObject
+ */
public static RbacView rbacViewFor(final String alias, final Class entityClass) {
return new RbacView(alias, entityClass);
}
@@ -76,22 +93,71 @@ public class RbacView {
entityAliases.put("global", new EntityAlias("global"));
}
+ /**
+ * Specifies, which columns of the restricted view are updatable at all.
+ *
+ * @param columnNames
+ * A list of the updatable columns.
+ *
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView withUpdatableColumns(final String... columnNames) {
Collections.addAll(updatableColumns, columnNames);
verifyVersionColumnExists();
return this;
}
+ /** Specifies the SQL query which creates the identity view for this entity.
+ *
+ * An identity view is a view which maps an objectUuid to an idName.
+ * The idName should be a human-readable representation of the row, but as short as possible.
+ * The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'.
+ * It's used to create the object-specific-role-names like test_customer#abc.admin - here 'abc' is the idName.
+ * The idName not necessarily unique in a table, but it should be avoided.
+ *
+ *
+ * @param sqlExpression
+ * Either specify an SQL projection (the part between SELECT and FROM), e.g. `SQL.projection("columnName")
+ * or the whole SELECT query returning the uuid and idName columns,
+ * e.g. `SQL.query("SELECT ... AS uuid, ... AS idName FROM ... JOIN ...").
+ * Only add really important columns, just enough to create a short human-readable representation.
+ *
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView withIdentityView(final SQL sqlExpression) {
this.identityViewSqlQuery = sqlExpression;
return this;
}
+ /**
+ * Specifies a ORDER BY clause for the generated restricted view.
+ *
+ * A restricted view is generated, no matter if the order was specified or not.
+ *
+ * @param orderBySqlExpression
+ * That's the part behind `ORDER BY`, e.g. `SQL.expression("prefix").
+ *
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView withRestrictedViewOrderBy(final SQL orderBySqlExpression) {
this.orderBySqlExpression = orderBySqlExpression;
return this;
}
+ /**
+ * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table.
+ *
+ * @param role
+ * OWNER, ADMIN, AGENT etc.
+ * @param with
+ * a lambda which receives the created role to create grants and permissions to and from the newly created role,
+ * e.g. the owning user, incoming superroles, outgoing subroles
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView createRole(final Role role, final Consumer with) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
with.accept(newRoleDef);
@@ -99,6 +165,15 @@ public class RbacView {
return this;
}
+ /**
+ * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
+ * which is becomes sub-role of the previously created role.
+ *
+ * @param role
+ * OWNER, ADMIN, AGENT etc.
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView createSubRole(final Role role) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
@@ -106,6 +181,19 @@ public class RbacView {
return this;
}
+
+ /**
+ * Specifies that the given role (OWNER, ADMIN, ...) is to be created for new/updated roles in this table,
+ * which is becomes sub-role of the previously created role.
+ *
+ * @param role
+ * OWNER, ADMIN, AGENT etc.
+ * @param with
+ * a lambda which receives the created role to create grants and permissions to and from the newly created role,
+ * e.g. the owning user, incoming superroles, outgoing subroles
+ * @return
+ * the `this` instance itself to allow chained calls.
+ */
public RbacView createSubRole(final Role role, final Consumer with) {
final RbacRoleDefinition newRoleDef = findRbacRole(rootEntityAlias, role).toCreate();
findOrCreateGrantDef(newRoleDef, previousRoleDef).toCreate();
@@ -114,10 +202,38 @@ public class RbacView {
return this;
}
+ /**
+ * Specifies that the given permission is to be created for each new row in the target table.
+ *
+ * Grants to permissions created by this method have to be specified separately,
+ * often it's easier to read to use createRole/createSubRole and use with.permission(...).
+ *
+ * @param permission
+ * e.g. INSERT, SELECT, UPDATE, DELETE
+ *
+ * @return
+ * the newly created permission definition
+ */
public RbacPermissionDefinition createPermission(final Permission permission) {
return createPermission(rootEntityAlias, permission);
}
+ /**
+ * Specifies that the given permission is to be created for each new row in the target table,
+ * but for another table, e.g. a table with details data with different access rights.
+ *
+ * Grants to permissions created by this method have to be specified separately,
+ * often it's easier to read to use createRole/createSubRole and use with.permission(...).
+ *
+ * @param entityAliasName
+ * A previously defined entity alias name.
+ *
+ * @param permission
+ * e.g. INSERT, SELECT, UPDATE, DELETE
+ *
+ * @return
+ * the newly created permission definition
+ */
public RbacPermissionDefinition createPermission(final String entityAliasName, final Permission permission) {
return createPermission(findEntityAlias(entityAliasName), permission);
}
@@ -133,6 +249,32 @@ public class RbacView {
return this;
}
+ /**
+ * Imports the RBAC template from the given entity class and defines an alias name for it.
+ * This method is especially for proxy-entities, if the root entity does not have its own
+ * roles, a proxy-entity can be specified and its roles can be used instead.
+ *
+ * @param aliasName
+ * An alias name for the entity class. The same entity class can be imported multiple times,
+ * if multiple references to its table exist, then distinct alias names habe to be defined.
+ *
+ * @param entityClass
+ * A JPA entity class extending RbacObject which also implements an `rbac` method returning
+ * its RBAC specification.
+ *
+ * @param fetchSql
+ * An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
+ * newly created or updated row (will be replaced by NEW/OLD from the trigger method).
+ *
+ * @param dependsOnColum
+ * The column, usually containing an uuid, on which this other table depends.
+ *
+ * @return
+ * the newly created permission definition
+ *
+ * @param
+ * a JPA entity class extending RbacObject
+ */
public RbacView importRootEntityAliasProxy(
final String aliasName,
final Class extends HasUuid> entityClass,
@@ -141,35 +283,75 @@ public class RbacView {
if (rootEntityAliasProxy != null) {
throw new IllegalStateException("there is already an entityAliasProxy: " + rootEntityAliasProxy);
}
- rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
+ rootEntityAliasProxy = importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, NOT_NULL);
return this;
}
+ /**
+ * Imports the RBAC template from the given entity class and defines an alias name for it.
+ * This method is especially to declare sub-entities, e.g. details to a main object.
+ *
+ * @see {@link}
+ *
+ * @return
+ * the newly created permission definition
+ *
+ * @param
+ * a JPA entity class extending RbacObject
+ */
public RbacView importSubEntityAlias(
final String aliasName, final Class extends HasUuid> entityClass,
final SQL fetchSql, final Column dependsOnColum) {
- importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true);
+ importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
return this;
}
+ /**
+ * Imports the RBAC template from the given entity class and defines an anlias name for it.
+ *
+ * @param aliasName
+ * An alias name for the entity class. The same entity class can be imported multiple times,
+ * if multiple references to its table exist, then distinct alias names habe to be defined.
+ *
+ * @param entityClass
+ * A JPA entity class extending RbacObject which also implements an `rbac` method returning
+ * its RBAC specification.
+ *
+ * @param fetchSql
+ * An SQL SELECT statement which fetches the referenced row. Use `${REF}` to speficiy the
+ * newly created or updated row (will be replaced by NEW/OLD from the trigger method).
+ *
+ * @param dependsOnColum
+ * The column, usually containing an uuid, on which this other table depends.
+ *
+ * @param nullable
+ * Specifies whether the dependsOnColum is nullable or not.
+ *
+ * @return
+ * the newly created permission definition
+ *
+ * @param
+ * a JPA entity class extending RbacObject
+ */
public RbacView importEntityAlias(
final String aliasName, final Class extends HasUuid> entityClass,
- final Column dependsOnColum, final SQL fetchSql) {
- importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false);
+ final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
+ importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
return this;
}
+ // TODO: remove once it's not used in HsOffice...Entity anymore
public RbacView importEntityAlias(
final String aliasName, final Class extends HasUuid> entityClass,
final Column dependsOnColum) {
- importEntityAliasImpl(aliasName, entityClass, autoFetched(), dependsOnColum, false);
+ importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this;
}
private EntityAlias importEntityAliasImpl(
final String aliasName, final Class extends HasUuid> entityClass,
- final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity) {
- final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity);
+ final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
+ final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, entityAlias);
try {
importAsAlias(aliasName, rbacDefinition(entityClass), asSubEntity);
@@ -224,6 +406,16 @@ public class RbacView {
}
}
+ /**
+ * Starts declaring a grant to a given role.
+ *
+ * @param entityAlias
+ * A previously speciried entity alias name.
+ * @param role
+ * OWNER, ADMIN, AGENT, ...
+ * @return
+ * a grant builder
+ */
public RbacGrantBuilder toRole(final String entityAlias, final Role role) {
return new RbacGrantBuilder(entityAlias, role);
}
@@ -281,15 +473,19 @@ public class RbacView {
return RbacView.this;
}
- public RbacView grantPermission(final String entityAliasName, final Permission perm) {
- final var entityAlias = findEntityAlias(entityAliasName);
- final var forTable = entityAlias.getRawTableName();
- findOrCreateGrantDef(findRbacPerm(entityAlias, perm, forTable), superRoleDef).toCreate();
+ public RbacView grantPermission(final Permission perm) {
+ final var forTable = rootEntityAlias.getRawTableName();
+ findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate();
return RbacView.this;
}
}
+ public enum Nullable {
+ NOT_NULL, // DEFAULT
+ NULLABLE
+ }
+
@Getter
@EqualsAndHashCode
public class RbacGrantDefinition {
@@ -418,6 +614,16 @@ public class RbacView {
permDefs.add(this);
}
+ /**
+ * Grants the permission under definition to the given role.
+ *
+ * @param entityAlias
+ * A previously declared entity alias name.
+ * @param role
+ * OWNER, ADMIN, ...
+ * @return
+ * The RbacView specification to which this permission definition belongs.
+ */
public RbacView grantedTo(final String entityAlias, final Role role) {
findOrCreateGrantDef(this, findRbacRole(entityAlias, role)).toCreate();
return RbacView.this;
@@ -448,19 +654,61 @@ public class RbacView {
return this;
}
+ /**
+ * Specifies which user becomes the owner of newly created objects.
+ * @param userRole
+ * GLOBAL_ADMIN, CREATOR, ...
+ * @return
+ * The grant definition for further chained calls.
+ */
public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
return grantRoleToUser(this, findUserRef(userRole));
}
+ /**
+ * Specifies which permission is to be created for newly created objects.
+ * @param permission
+ * INSERT, SELECT, ...
+ * @return
+ * The grant definition for further chained calls.
+ */
public RbacGrantDefinition permission(final Permission permission) {
return grantPermissionToRole(createPermission(entityAlias, permission), this);
}
+ /**
+ * Specifies in incoming super role which gets granted the role under definition.
+ *
+ * Incoming means an incoming grant arrow in our grant-diagrams.
+ * Super-role means that it's the role to which another role is granted.
+ * Both means actually the same, just in different aspects.
+ *
+ * @param entityAlias
+ * A previously declared entity alias name.
+ * @param role
+ * OWNER, ADMIN, ...
+ * @return
+ * The grant definition for further chained calls.
+ */
public RbacGrantDefinition incomingSuperRole(final String entityAlias, final Role role) {
final var incomingSuperRole = findRbacRole(entityAlias, role);
return grantSubRoleToSuperRole(this, incomingSuperRole);
}
+ /**
+ * Specifies in outgoing sub role which gets granted the role under definition.
+ *
+ * Outgoing means an outgoing grant arrow in our grant-diagrams.
+ * Sub-role means which is granted to another role.
+ * Both means actually the same, just in different aspects.
+ *
+ * @param entityAlias
+ * A previously declared entity alias name.
+ * @param role
+ * OWNER, ADMIN, ...
+ * @return
+ * The grant definition for further chained calls.
+ */
public RbacGrantDefinition outgoingSubRole(final String entityAlias, final Role role) {
final var outgoingSubRole = findRbacRole(entityAlias, role);
return grantSubRoleToSuperRole(outgoingSubRole, this);
@@ -560,14 +808,14 @@ public class RbacView {
.orElseGet(() -> new RbacGrantDefinition(subRoleDefinition, superRoleDefinition));
}
- record EntityAlias(String aliasName, Class extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity) {
+ record EntityAlias(String aliasName, Class extends RbacObject> entityClass, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
public EntityAlias(final String aliasName) {
- this(aliasName, null, null, null, false);
+ this(aliasName, null, null, null, false, null);
}
public EntityAlias(final String aliasName, final Class extends RbacObject> entityClass) {
- this(aliasName, entityClass, null, null, false);
+ this(aliasName, entityClass, null, null, false, null);
}
boolean isGlobal() {
@@ -592,8 +840,8 @@ public class RbacView {
};
}
- public boolean hasFetchSql() {
- return fetchSql != null;
+ boolean isFetchedByDirectForeignKey() {
+ return fetchSql != null && fetchSql.part == AUTO_FETCH;
}
private String withoutEntitySuffix(final String simpleEntityName) {
@@ -626,39 +874,35 @@ public class RbacView {
return tableName.substring(0, tableName.length() - "_rv".length());
}
- public record Role(String roleName) {
+ public enum Role {
- public static final Role OWNER = new Role("owner");
- public static final Role ADMIN = new Role("admin");
- public static final Role AGENT = new Role("agent");
- public static final Role TENANT = new Role("tenant");
- public static final Role REFERRER = new Role("referrer");
+ OWNER,
+ ADMIN,
+ AGENT,
+ TENANT,
+ REFERRER,
+
+ GUEST;
@Override
public String toString() {
- return ":" + roleName;
+ return ":" + roleName();
}
- @Override
- public boolean equals(final Object obj) {
- return ((obj instanceof Role) && ((Role) obj).roleName.equals(this.roleName));
+ String roleName() {
+ return name().toLowerCase();
}
}
- public record Permission(String permission) {
-
- public static final Permission INSERT = new Permission("INSERT");
- public static final Permission DELETE = new Permission("DELETE");
- public static final Permission UPDATE = new Permission("UPDATE");
- public static final Permission SELECT = new Permission("SELECT");
-
- public static Permission custom(final String permission) {
- return new Permission(permission);
- }
+ public enum Permission {
+ INSERT,
+ DELETE,
+ UPDATE,
+ SELECT;
@Override
public String toString() {
- return ":" + permission;
+ return ":" + name();
}
}
@@ -666,14 +910,25 @@ public class RbacView {
/**
* DSL method to specify an SQL SELECT expression which fetches the related entity,
- * using the reference `${ref}` of the root entity.
- * `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
- * `into ...` will be added with a variable name prefixed with either `new` or `old`.
+ * using the reference `${ref}` of the root entity and `${columns}` for the projection.
+ *
+ * The query must define the entity alias name of the fetched table
+ * as its alias for, so it can be used in the generated projection (the columns between
+ * `SELECT` and `FROM`.
+ *
+ * `${ref}` is going to be replaced by either `NEW` or `OLD` of the trigger function.
+ * `into ...` will be added with a variable name prefixed with either `new` or `old`.
+ *
+ * `${columns}` is going to be replaced by the columns which are needed for the query,
+ * e.g. `*` or `uuid`.
*
* @param sql an SQL SELECT expression (not ending with ';)
* @return the wrapped SQL expression
*/
public static SQL fetchedBySql(final String sql) {
+ if ( !sql.startsWith("SELECT ${columns}") ) {
+ throw new IllegalArgumentException("SQL SELECT expression must start with 'SELECT ${columns}', but is: " + sql);
+ }
validateExpression(sql);
return new SQL(sql, Part.SQL_QUERY);
}
@@ -685,8 +940,8 @@ public class RbacView {
*
* @return the wrapped SQL definition object
*/
- public static SQL autoFetched() {
- return new SQL(null, Part.AUTO_FETCH);
+ public static SQL directlyFetchedByDependsOnColumn() {
+ return new SQL(null, AUTO_FETCH);
}
/**
@@ -794,6 +1049,26 @@ public class RbacView {
}
}
+ private static void generateRbacView(final Class extends HasUuid> c) {
+ final Method mainMethod = stream(c.getMethods()).filter(
+ m -> isStatic(m.getModifiers()) && m.getName().equals("main")
+ )
+ .findFirst()
+ .orElse(null);
+ if (mainMethod != null) {
+ try {
+ mainMethod.invoke(null, new Object[] { null });
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
+ }
+ }
+
+ /**
+ * This main method generates the RbacViews (PostgreSQL+diagram) for all given entity classes.
+ */
public static void main(String[] args) {
Stream.of(
TestCustomerEntity.class,
@@ -810,21 +1085,6 @@ public class RbacView {
HsOfficeSepaMandateEntity.class,
HsOfficeCoopSharesTransactionEntity.class,
HsOfficeMembershipEntity.class
- ).forEach(c -> {
- final Method mainMethod = stream(c.getMethods()).filter(
- m -> isStatic(m.getModifiers()) && m.getName().equals("main")
- )
- .findFirst()
- .orElse(null);
- if (mainMethod != null) {
- try {
- mainMethod.invoke(null, new Object[] { null });
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new RuntimeException(e);
- }
- } else {
- System.err.println("no main method in: " + c.getName());
- }
- });
+ ).forEach(RbacView::generateRbacView);
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
index ccef566d..d6a9bc28 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
@@ -4,7 +4,6 @@ import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import java.nio.file.*;
-import java.time.LocalDateTime;
import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
@@ -149,14 +148,13 @@ public class RbacViewMermaidFlowchartGenerator {
"""
### rbac %{entityAlias}
- This code generated was by RbacViewMermaidFlowchartGenerator at %{timestamp}.
+ This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
%{flowchart}
```
"""
.replace("%{entityAlias}", rbacDef.getRootEntityAlias().aliasName())
- .replace("%{timestamp}", LocalDateTime.now().toString())
.replace("%{flowchart}", flowchart.toString()),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("Markdown-File: " + path.toAbsolutePath());
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
index eb8f3534..5a3b2be8 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewPostgresGenerator.java
@@ -5,7 +5,6 @@ import lombok.SneakyThrows;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
-import java.time.LocalDateTime;
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
@@ -21,10 +20,9 @@ public class RbacViewPostgresGenerator {
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
plPgSql.writeLn("""
--liquibase formatted sql
- -- This code generated was by ${generator} at ${timestamp}.
+ -- This code generated was by ${generator}, do not amend manually.
""",
with("generator", getClass().getSimpleName()),
- with("timestamp", LocalDateTime.now().toString()),
with("ref", NEW.name()));
new RbacObjectGenerator(rbacDef, liqibaseTagPrefix).generateTo(plPgSql);
@@ -37,8 +35,11 @@ public class RbacViewPostgresGenerator {
@Override
public String toString() {
- return plPgSql.toString();
-}
+ return plPgSql.toString()
+ .replace("\n\n\n", "\n\n")
+ .replace("-- ====", "\n-- ====")
+ .replace("\n\n--//", "\n--//");
+ }
@SneakyThrows
public void generateToChangeLog(final Path outputPath) {
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
index edb1f609..719c8ab4 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
@@ -82,6 +83,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn("begin");
plPgSql.indented(() -> {
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
+ plPgSql.writeLn();
generateCreateRolesAndGrantsAfterInsert(plPgSql);
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
@@ -90,6 +92,37 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn();
}
+
+ private void generateSimplifiedUpdateTriggerFunction(final StringWriter plPgSql) {
+
+ final var updateConditions = updatableEntityAliases()
+ .map(RbacView.EntityAlias::dependsOnColumName)
+ .distinct()
+ .map(columnName -> "NEW." + columnName + " is distinct from OLD." + columnName)
+ .collect(joining( "\n or "));
+ plPgSql.writeLn("""
+ /*
+ Called from the AFTER UPDATE TRIGGER to re-wire the grants.
+ */
+
+ create or replace procedure updateRbacRulesFor${simpleEntityName}(
+ OLD ${rawTableName},
+ NEW ${rawTableName}
+ )
+ language plpgsql as $$
+ begin
+
+ if ${updateConditions} then
+ delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
+ call buildRbacSystemFor${simpleEntityName}(NEW);
+ end if;
+ end; $$;
+ """,
+ with("simpleEntityName", simpleEntityName),
+ with("rawTableName", rawTableName),
+ with("updateConditions", updateConditions));
+ }
+
private void generateUpdateTriggerFunction(final StringWriter plPgSql) {
plPgSql.writeLn("""
/*
@@ -109,7 +142,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.chopEmptyLines();
plPgSql.indented(() -> {
- updatableEntityAliases()
+ referencedEntityAliases()
.forEach((ea) -> {
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
@@ -120,6 +153,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn("begin");
plPgSql.indented(() -> {
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
+ plPgSql.writeLn();
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
@@ -132,11 +166,18 @@ class RolesGrantsAndPermissionsGenerator {
return updatableEntityAliases().anyMatch(e -> true);
}
+ private boolean hasAnyUpdatableAndNullableEntityAliases() {
+ return updatableEntityAliases()
+ .filter(ea -> ea.nullable() == RbacView.Nullable.NULLABLE)
+ .anyMatch(e -> true);
+ }
+
private void generateCreateRolesAndGrantsAfterInsert(final StringWriter plPgSql) {
referencedEntityAliases()
- .forEach((ea) -> plPgSql.writeLn(
- ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
- with("ref", NEW.name())));
+ .forEach((ea) -> {
+ generateFetchedVars(plPgSql, ea, NEW);
+ plPgSql.writeLn();
+ });
createRolesWithGrantsSql(plPgSql, OWNER);
createRolesWithGrantsSql(plPgSql, ADMIN);
@@ -165,14 +206,11 @@ class RolesGrantsAndPermissionsGenerator {
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
plPgSql.ensureSingleEmptyLine();
- updatableEntityAliases()
+ referencedEntityAliases()
.forEach((ea) -> {
- plPgSql.writeLn(
- ea.fetchSql().sql + " into " + entityRefVar(OLD, ea) + ";",
- with("ref", OLD.name()));
- plPgSql.writeLn(
- ea.fetchSql().sql + " into " + entityRefVar(NEW, ea) + ";",
- with("ref", NEW.name()));
+ generateFetchedVars(plPgSql, ea, OLD);
+ generateFetchedVars(plPgSql, ea, NEW);
+ plPgSql.writeLn();
});
updatableEntityAliases()
@@ -190,14 +228,29 @@ class RolesGrantsAndPermissionsGenerator {
});
}
- private boolean isUpdatable(final RbacView.Column c) {
- return rbacDef.getUpdatableColumns().contains(c);
+ private void generateFetchedVars(
+ final StringWriter plPgSql,
+ final RbacView.EntityAlias ea,
+ final PostgresTriggerReference old) {
+ plPgSql.writeLn(
+ ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
+ with("columns", ea.aliasName() + ".*"),
+ with("ref", old.name()));
+ if (ea.nullable() == RbacView.Nullable.NOT_NULL) {
+ plPgSql.writeLn(
+ "assert ${entityRefVar}.uuid is not null, format('${entityRefVar} must not be null for ${REF}.${dependsOnColumn} = %s', ${REF}.${dependsOnColumn});",
+ with("entityRefVar", entityRefVar(old, ea)),
+ with("dependsOnColumn", ea.dependsOnColumName()),
+ with("ref", old.name()));
+ plPgSql.writeLn();
+ }
}
private void updateGrantsDependingOn(final StringWriter plPgSql, final String columnName) {
rbacDef.getGrantDefs().stream()
.filter(RbacView.RbacGrantDefinition::isToCreate)
.filter(g -> g.dependsOnColumn(columnName))
+ .filter(g -> !isInsertPermissionGrant(g))
.forEach(g -> {
plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn(generateRevoke(g));
@@ -206,6 +259,11 @@ class RolesGrantsAndPermissionsGenerator {
});
}
+ private static Boolean isInsertPermissionGrant(final RbacView.RbacGrantDefinition g) {
+ final var isInsertPermissionGrant = ofNullable(g.getPermDef()).map(RbacPermissionDefinition::getPermission).map(p -> p == INSERT).orElse(false);
+ return isInsertPermissionGrant;
+ }
+
private void generateGrants(final StringWriter plPgSql, final RbacView.RbacGrantDefinition.GrantType grantType) {
plPgSql.ensureSingleEmptyLine();
rbacGrants.stream()
@@ -222,7 +280,7 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
- .replace("${permRef}", findPerm(OLD, grantDef.getPermDef()))
+ .replace("${permRef}", getPerm(OLD, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
};
}
@@ -246,6 +304,10 @@ class RolesGrantsAndPermissionsGenerator {
return permRef("findPermissionId", ref, permDef);
}
+ private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
+ return permRef("getPermissionId", ref, permDef);
+ }
+
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("createPermission", ref, permDef);
}
@@ -256,7 +318,7 @@ class RolesGrantsAndPermissionsGenerator {
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
? ref.name()
: refVarName(ref, permDef.entityAlias))
- .replace("${perm}", permDef.permission.permission());
+ .replace("${perm}", permDef.permission.name());
}
private String refVarName(final PostgresTriggerReference ref, final RbacView.EntityAlias entityAlias) {
@@ -301,12 +363,12 @@ class RolesGrantsAndPermissionsGenerator {
generatePermissionsForRole(plPgSql, role);
- generateUserGrantsForRole(plPgSql, role);
-
generateIncomingSuperRolesForRole(plPgSql, role);
generateOutgoingSubRolesForRole(plPgSql, role);
+ generateUserGrantsForRole(plPgSql, role);
+
plPgSql.chopTail(",\n");
plPgSql.writeLn();
});
@@ -333,7 +395,7 @@ class RolesGrantsAndPermissionsGenerator {
final var arrayElements = permissionGrantsForRole.stream()
.map(RbacView.RbacGrantDefinition::getPermDef)
.map(RbacPermissionDefinition::getPermission)
- .map(RbacView.Permission::permission)
+ .map(RbacView.Permission::name)
.map(p -> "'" + p + "'")
.sorted()
.toList();
@@ -348,7 +410,7 @@ class RolesGrantsAndPermissionsGenerator {
if (!incomingGrants.isEmpty()) {
final var arrayElements = incomingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSuperRoleDef(), g.isAssumed()))
- .toList();
+ .sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("incomingSuperRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(incomingGrants);
@@ -360,7 +422,7 @@ class RolesGrantsAndPermissionsGenerator {
if (!outgoingGrants.isEmpty()) {
final var arrayElements = outgoingGrants.stream()
.map(g -> toPlPgSqlReference(NEW, g.getSubRoleDef(), g.isAssumed()))
- .toList();
+ .sorted().toList();
plPgSql.indented(() ->
plPgSql.writeLn("outgoingSubRoles => array[" + joinArrayElements(arrayElements, 1) + "],\n"));
rbacGrants.removeAll(outgoingGrants);
@@ -444,7 +506,11 @@ class RolesGrantsAndPermissionsGenerator {
private void generateUpdateTrigger(final StringWriter plPgSql) {
generateHeader(plPgSql, "update");
- generateUpdateTriggerFunction(plPgSql);
+ if ( hasAnyUpdatableAndNullableEntityAliases() ) {
+ generateSimplifiedUpdateTriggerFunction(plPgSql);
+ } else {
+ generateUpdateTriggerFunction(plPgSql);
+ }
plPgSql.writeLn("""
/*
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
index 512ec72d..fe4b0548 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/StringWriter.java
@@ -38,12 +38,26 @@ public class StringWriter {
--indentLevel;
}
+ void indent(int levels) {
+ indentLevel += levels;
+ }
+
+ void unindent(int levels) {
+ indentLevel -= levels;
+ }
+
void indented(final Runnable indented) {
indent();
indented.run();
unindent();
}
+ void indented(int levels, final Runnable indented) {
+ indent(levels);
+ indented.run();
+ unindent(levels);
+ }
+
boolean chopTail(final String tail) {
if (string.toString().endsWith(tail)) {
string.setLength(string.length() - tail.length());
@@ -68,7 +82,7 @@ public class StringWriter {
return string.toString();
}
- public static String indented(final String text, final int indentLevel) {
+ public static String indented(final int indentLevel, final String text) {
final var indentation = StringUtils.repeat(" ", indentLevel);
final var indented = stream(text.split("\n"))
.map(line -> line.trim().isBlank() ? "" : indentation + line)
@@ -80,7 +94,7 @@ public class StringWriter {
if ( indentLevel == 0) {
return text;
}
- return indented(text, indentLevel);
+ return indented(indentLevel, text);
}
record VarDef(String name, String value){}
@@ -95,17 +109,13 @@ public class StringWriter {
}
String apply(final String textToAppend) {
- try {
- text = textToAppend;
- stream(varDefs).forEach(varDef -> {
- final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
- final var matcher = pattern.matcher(text);
- text = matcher.replaceAll(varDef.value());
- });
- return text;
- } catch (Exception exc) {
- throw exc;
- }
- }
+ text = textToAppend;
+ stream(varDefs).forEach(varDef -> {
+ final var pattern = Pattern.compile("\\$\\{" + varDef.name() + "}", Pattern.CASE_INSENSITIVE);
+ final var matcher = pattern.matcher(text);
+ text = matcher.replaceAll(varDef.value());
+ });
+ return text;
}
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java
index 29bdc2d8..9dfaea74 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantController.java
@@ -94,4 +94,17 @@ public class RbacGrantController implements RbacGrantsApi {
return ResponseEntity.noContent().build();
}
+
+// TODO: implement an endpoint to create a Mermaid flowchart with all grants of a given user
+// @GetMapping(
+// path = "/api/rbac/users/{userUuid}/grants",
+// produces = {"text/vnd.mermaid"})
+// @Transactional(readOnly = true)
+// public ResponseEntity allGrantsOfUserAsMermaid(
+// @RequestHeader(name = "current-user") String currentUser,
+// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
+// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
+// return ResponseEntity.ok(graph);
+// }
+
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
index 0296cd61..cf05496a 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
@@ -21,6 +21,8 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.
@Service
public class RbacGrantsDiagramService {
+ private static final int GRANT_LIMIT = 500;
+
public static void writeToFile(final String title, final String graph, final String fileName) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
@@ -42,7 +44,11 @@ public class RbacGrantsDiagramService {
PERMISSIONS,
NOT_ASSUMED,
TEST_ENTITIES,
- NON_TEST_ENTITIES
+ NON_TEST_ENTITIES;
+
+ public static final EnumSet ALL = EnumSet.allOf(Include.class);
+ public static final EnumSet ALL_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, TEST_ENTITIES, PERMISSIONS);
+ public static final EnumSet ALL_NON_TEST_ENTITY_RELATED = EnumSet.of(USERS, DETAILS, NOT_ASSUMED, NON_TEST_ENTITIES, PERMISSIONS);
}
@Autowired
@@ -55,7 +61,7 @@ public class RbacGrantsDiagramService {
private EntityManager em;
public String allGrantsToCurrentUser(final EnumSet includes) {
- final var graph = new HashSet();
+ final var graph = new LimitedHashSet();
for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
traverseGrantsTo(graph, subjectUuid, includes);
}
@@ -88,7 +94,7 @@ public class RbacGrantsDiagramService {
.setParameter("targetObject", targetObject)
.setParameter("op", op)
.getSingleResult();
- final var graph = new HashSet();
+ final var graph = new LimitedHashSet();
traverseGrantsFrom(graph, refUuid, includes);
return toMermaidFlowchart(graph, includes);
}
@@ -116,7 +122,7 @@ public class RbacGrantsDiagramService {
)
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
.entrySet().stream()
- .map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
+ .map(entity -> "subgraph " + cleanId(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
+ entity.getValue().stream()
.map(n -> renderNode(n.idName(), n.uuid()).replace("\n", "\n "))
.sorted()
@@ -127,14 +133,15 @@ public class RbacGrantsDiagramService {
: "";
final var grants = graph.stream()
- .map(g -> quoted(g.getAscendantIdName())
+ .map(g -> cleanId(g.getAscendantIdName())
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
- + quoted(g.getDescendantIdName()))
+ + cleanId(g.getDescendantIdName()))
.sorted()
.collect(joining("\n"));
final var avoidCroppedNodeLabels = "%%{init:{'flowchart':{'htmlLabels':false}}}%%\n\n";
return (includes.contains(DETAILS) ? avoidCroppedNodeLabels : "")
+ + (graph.size() >= GRANT_LIMIT ? "%% too many grants, graph is cropped\n" : "")
+ "flowchart TB\n\n"
+ entities
+ grants;
@@ -151,7 +158,7 @@ public class RbacGrantsDiagramService {
// }
// return "[" + table + "\n" + entity + "]";
// }
- return "[" + entityId + "]";
+ return "[" + cleanId(entityId) + "]";
}
private static String renderEntityIdName(final Node node) {
@@ -170,7 +177,7 @@ public class RbacGrantsDiagramService {
}
private String renderNode(final String idName, final UUID uuid) {
- return quoted(idName) + renderNodeContent(idName, uuid);
+ return cleanId(idName) + renderNodeContent(idName, uuid);
}
private String renderNodeContent(final String idName, final UUID uuid) {
@@ -196,9 +203,24 @@ public class RbacGrantsDiagramService {
}
@NotNull
- private static String quoted(final String idName) {
- return idName.replace(" ", ":").replaceAll("@.*", "");
+ private static String cleanId(final String idName) {
+ return idName.replace(" ", ":").replaceAll("@.*", "")
+ .replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
}
+
+
+ class LimitedHashSet extends HashSet {
+
+ @Override
+ public boolean add(final T t) {
+ if (size() < GRANT_LIMIT ) {
+ return super.add(t);
+ } else {
+ return false;
+ }
+ }
+ }
+
}
record Node(String idName, UUID uuid) {
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
index 153344fa..fa5b16aa 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
@@ -1,5 +1,5 @@
package net.hostsharing.hsadminng.rbac.rbacrole;
public enum RbacRoleType {
- owner, admin, agent, tenant, guest
+ owner, admin, agent, tenant, guest, referrer
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
index 99b0fb3c..b4152fa9 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
@@ -41,8 +41,7 @@ public class TestCustomerEntity implements HasUuid {
.withIdentityView(SQL.projection("prefix"))
.withRestrictedViewOrderBy(SQL.expression("reference"))
.withUpdatableColumns("reference", "prefix", "adminUserName")
- // TODO: do we want explicit specification of parent-independent insert permissions?
- // .toRole("global", ADMIN).grantPermission("customer", INSERT)
+ .toRole("global", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR).unassumed();
diff --git a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
index 6a031df7..70626f89 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
@@ -14,9 +14,10 @@ import java.io.IOException;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity
@@ -49,11 +50,9 @@ public class TestDomainEntity implements HasUuid {
.importEntityAlias("package", TestPackageEntity.class,
dependsOnColumn("packageUuid"),
- fetchedBySql("""
- SELECT * FROM test_package p
- WHERE p.uuid= ${ref}.packageUuid
- """))
- .toRole("package", ADMIN).grantPermission("domain", INSERT)
+ directlyFetchedByDependsOnColumn(),
+ NOT_NULL)
+ .toRole("package", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> {
with.incomingSuperRole("package", ADMIN);
diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
index 757fcf05..8f72fc4c 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
@@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
@@ -50,11 +51,9 @@ public class TestPackageEntity implements HasUuid {
.importEntityAlias("customer", TestCustomerEntity.class,
dependsOnColumn("customerUuid"),
- fetchedBySql("""
- SELECT * FROM test_customer c
- WHERE c.uuid= ${ref}.customerUuid
- """))
- .toRole("customer", ADMIN).grantPermission("package", INSERT)
+ directlyFetchedByDependsOnColumn(),
+ NOT_NULL)
+ .toRole("customer", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> {
with.incomingSuperRole("customer", ADMIN);
diff --git a/src/main/resources/db/changelog/007-table-columns.sql b/src/main/resources/db/changelog/007-table-columns.sql
new file mode 100644
index 00000000..588defba
--- /dev/null
+++ b/src/main/resources/db/changelog/007-table-columns.sql
@@ -0,0 +1,20 @@
+--liquibase formatted sql
+
+
+-- ============================================================================
+-- TABLE-COLUMNS-FUNCTION
+--changeset table-columns-function:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+create or replace function columnsNames( tableName text )
+ returns text
+ stable
+ language 'plpgsql' as $$
+declare columns text[];
+begin
+ columns := (select array(select column_name::text
+ from information_schema.columns
+ where table_name = tableName));
+ return array_to_string(columns, ', ');
+end; $$
+--//
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql
index 8de41891..0e5cc457 100644
--- a/src/main/resources/db/changelog/010-context.sql
+++ b/src/main/resources/db/changelog/010-context.sql
@@ -160,6 +160,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
declare
cleanIdentifier varchar;
begin
+ -- TODO: remove the ':' from the list of allowed characters as soon as it's not used anymore
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
return cleanIdentifier;
end; $$;
diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql
index 2992d6a9..ca560bf9 100644
--- a/src/main/resources/db/changelog/050-rbac-base.sql
+++ b/src/main/resources/db/changelog/050-rbac-base.sql
@@ -164,7 +164,7 @@ end; $$;
*/
-create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest');
+create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest', 'referrer');
create table RbacRole
(
@@ -373,10 +373,12 @@ create table RbacPermission
uuid uuid primary key references RbacReference (uuid) on delete cascade,
objectUuid uuid not null references RbacObject,
op RbacOp not null,
- opTableName varchar(60),
- unique (objectUuid, op)
+ opTableName varchar(60)
);
+ALTER TABLE RbacPermission
+ ADD CONSTRAINT RbacPermission_uc UNIQUE NULLS NOT DISTINCT (objectUuid, op, opTableName);
+
call create_journal('RbacPermission');
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
@@ -395,7 +397,10 @@ begin
raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
end if;
- permissionUuid = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = forOp and opTableName = forOpTableName);
+ permissionUuid := (
+ select uuid from RbacPermission
+ where objectUuid = forObjectUuid
+ and op = forOp and opTableName is not distinct from forOpTableName);
if (permissionUuid is null) then
insert into RbacReference ("type")
values ('RbacPermission')
@@ -466,8 +471,44 @@ select uuid
and p.op = forOp
and p.opTableName = forOpTableName
$$;
+
+create or replace function getPermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
+ returns uuid
+ stable -- leakproof
+ language plpgsql as $$
+declare
+ permissionUuid uuid;
+begin
+ select uuid into permissionUuid
+ from RbacPermission p
+ where p.objectUuid = forObjectUuid
+ and p.op = forOp
+ and forOpTableName is null or p.opTableName = forOpTableName;
+ assert permissionUuid is not null,
+ format('permission %s %s for object UUID %s cannot be found', forOp, forOpTableName, forObjectUuid);
+ return permissionUuid;
+end; $$;
--//
+
+-- ============================================================================
+--changeset rbac-base-duplicate-role-grant-exception:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+create or replace procedure raiseDuplicateRoleGrantException(subRoleId uuid, superRoleId uuid)
+ language plpgsql as $$
+declare
+ subRoleIdName text;
+ superRoleIdName text;
+begin
+ select roleIdName from rbacRole_ev where uuid=subRoleId into subRoleIdName;
+ select roleIdName from rbacRole_ev where uuid=superRoleId into superRoleIdName;
+ raise exception '[400] Duplicate role grant detected: role % (%) already granted to % (%)', subRoleId, subRoleIdName, superRoleId, superRoleIdName;
+end;
+$$;
+--//
+
+
-- ============================================================================
--changeset rbac-base-GRANTS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@@ -588,7 +629,7 @@ select exists(
);
$$;
-create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid)
+create or replace procedure grantPermissionToRole(permissionUuid uuid, roleUuid uuid)
language plpgsql as $$
begin
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
@@ -601,10 +642,10 @@ begin
end;
$$;
-create or replace procedure grantPermissionToRole(roleDesc RbacRoleDescriptor, permissionUuid uuid)
+create or replace procedure grantPermissionToRole(permissionUuid uuid, roleDesc RbacRoleDescriptor)
language plpgsql as $$
begin
- call grantPermissionToRole(findRoleId(roleDesc), permissionUuid);
+ call grantPermissionToRole(permissionUuid, findRoleId(roleDesc));
end;
$$;
@@ -634,7 +675,7 @@ begin
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
if isGranted(subRoleId, superRoleId) then
- raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
+ call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
end if;
insert
@@ -650,6 +691,11 @@ declare
superRoleId uuid;
subRoleId uuid;
begin
+ -- TODO: maybe separate method grantRoleToRoleIfNotNull(...) for NULLABLE references
+ if superRole.objectUuid is null or subRole.objectuuid is null then
+ return;
+ end if;
+
superRoleId := findRoleId(superRole);
subRoleId := findRoleId(subRole);
@@ -657,7 +703,7 @@ begin
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
if isGranted(subRoleId, superRoleId) then
- raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
+ call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
end if;
insert
@@ -672,6 +718,7 @@ declare
superRoleId uuid;
subRoleId uuid;
begin
+ if ( superRoleId is null ) then return; end if;
superRoleId := findRoleId(superRole);
if ( subRoleId is null ) then return; end if;
subRoleId := findRoleId(subRole);
@@ -680,7 +727,7 @@ begin
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
if isGranted(subRoleId, superRoleId) then
- raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
+ call raiseDuplicateRoleGrantException(subRoleId, superRoleId);
end if;
insert
@@ -704,11 +751,39 @@ begin
if (isGranted(superRoleId, subRoleId)) then
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
else
- raise exception 'cannot revoke role % (%) from % (% because it is not granted',
+ raise exception 'cannot revoke role % (%) from % (%) because it is not granted',
subRole, subRoleId, superRole, superRoleId;
end if;
end; $$;
+create or replace procedure revokePermissionFromRole(permissionId UUID, superRole RbacRoleDescriptor)
+ language plpgsql as $$
+declare
+ superRoleId uuid;
+ permissionOp text;
+ objectTable text;
+ objectUuid uuid;
+begin
+ superRoleId := findRoleId(superRole);
+
+ perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole');
+ perform assertReferenceType('permission (descendant)', permissionId, 'RbacPermission');
+
+ if (isGranted(superRoleId, permissionId)) then
+ delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = permissionId;
+ else
+ select p.op, o.objectTable, o.uuid
+ from rbacGrants g
+ join rbacPermission p on p.uuid=g.descendantUuid
+ join rbacobject o on o.uuid=p.objectUuid
+ where g.uuid=permissionId
+ into permissionOp, objectTable, objectUuid;
+
+ raise exception 'cannot revoke permission % (% on %#% (%) from % (%)) because it is not granted',
+ permissionId, permissionOp, objectTable, objectUuid, permissionId, superRole, superRoleId;
+ end if;
+end; $$;
+
-- ============================================================================
--changeset rbac-base-QUERY-ACCESSIBLE-OBJECT-UUIDS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/054-rbac-context.sql
index ede86057..5437131f 100644
--- a/src/main/resources/db/changelog/054-rbac-context.sql
+++ b/src/main/resources/db/changelog/054-rbac-context.sql
@@ -56,14 +56,17 @@ begin
roleTypeToAssume = split_part(roleNameParts, '#', 3);
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
+ if objectUuidToAssume is null then
+ raise exception '[401] object % cannot be found in table %', objectNameToAssume, objectTableToAssume;
+ end if;
- select uuid as roleuuidToAssume
+ select uuid
from RbacRole r
where r.objectUuid = objectUuidToAssume
and r.roleType = roleTypeToAssume
into roleUuidToAssume;
if roleUuidToAssume is null then
- raise exception '[403] role % not accessible for user %', roleName, currentSubjects();
+ raise exception '[403] role % does not exist or is not accessible for user %', roleName, currentUser();
end if;
if not isGranted(currentUserUuid, roleUuidToAssume) then
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
diff --git a/src/main/resources/db/changelog/057-rbac-role-builder.sql b/src/main/resources/db/changelog/057-rbac-role-builder.sql
index 1a7da953..57a97a2f 100644
--- a/src/main/resources/db/changelog/057-rbac-role-builder.sql
+++ b/src/main/resources/db/changelog/057-rbac-role-builder.sql
@@ -31,13 +31,13 @@ create or replace function createRoleWithGrants(
called on null input
language plpgsql as $$
declare
- roleUuid uuid;
- subRoleDesc RbacRoleDescriptor;
- superRoleDesc RbacRoleDescriptor;
- subRoleUuid uuid;
- superRoleUuid uuid;
- userUuid uuid;
- grantedByRoleUuid uuid;
+ roleUuid uuid;
+ subRoleDesc RbacRoleDescriptor;
+ superRoleDesc RbacRoleDescriptor;
+ subRoleUuid uuid;
+ superRoleUuid uuid;
+ userUuid uuid;
+ userGrantsByRoleUuid uuid;
begin
roleUuid := createRole(roleDescriptor);
@@ -58,14 +58,15 @@ begin
end loop;
if cardinality(userUuids) > 0 then
+ -- direct grants to users need a grantedByRole which can revoke the grant
if grantedByRole is null then
- grantedByRoleUuid := roleUuid;
+ userGrantsByRoleUuid := roleUuid; -- TODO: or do we want to require an explicit userGrantsByRoleUuid?
else
- grantedByRoleUuid := getRoleId(grantedByRole);
+ userGrantsByRoleUuid := getRoleId(grantedByRole);
end if;
foreach userUuid in array userUuids
loop
- call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
+ call grantRoleToUserUnchecked(userGrantsByRoleUuid, roleUuid, userUuid);
end loop;
end if;
diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql
index 89d585ea..efe71b1b 100644
--- a/src/main/resources/db/changelog/058-rbac-generators.sql
+++ b/src/main/resources/db/changelog/058-rbac-generators.sql
@@ -73,6 +73,7 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
end; $f$;
+ -- TODO: remove guest role
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
returns RbacRoleDescriptor
language plpgsql
@@ -81,6 +82,14 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
end; $f$;
+ create or replace function %1$sReferrer(entity %2$s)
+ returns RbacRoleDescriptor
+ language plpgsql
+ strict as $f$
+ begin
+ return roleDescriptor('%2$s', entity.uuid, 'referrer');
+ end; $f$;
+
$sql$, prefix, targetTable);
execute sql;
end; $$;
@@ -148,12 +157,16 @@ end; $$;
--changeset rbac-generators-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-create or replace procedure generateRbacRestrictedView(targetTable text, orderBy text, columnUpdates text = null)
+create or replace procedure generateRbacRestrictedView(targetTable text, orderBy text, columnUpdates text = null, columnNames text = '*')
language plpgsql as $$
declare
sql text;
+ newColumns text;
begin
targetTable := lower(targetTable);
+ if columnNames = '*' then
+ columnNames := columnsNames(targetTable);
+ end if;
/*
Creates a restricted view based on the 'SELECT' permission of the current subject.
@@ -175,20 +188,21 @@ begin
/**
Instead of insert trigger function for the restricted view.
*/
+ newColumns := 'new.' || replace(columnNames, ',', ', new.');
sql := format($sql$
- create or replace function %1$sInsert()
- returns trigger
- language plpgsql as $f$
- declare
- newTargetRow %1$s;
- begin
- insert
- into %1$s
- values (new.*)
- returning * into newTargetRow;
- return newTargetRow;
- end; $f$;
- $sql$, targetTable);
+ create or replace function %1$sInsert()
+ returns trigger
+ language plpgsql as $f$
+ declare
+ newTargetRow %1$s;
+ begin
+ insert
+ into %1$s (%2$s)
+ values (%3$s)
+ returning * into newTargetRow;
+ return newTargetRow;
+ end; $f$;
+ $sql$, targetTable, columnNames, newColumns);
execute sql;
/*
diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql
index 8313d05d..f8058113 100644
--- a/src/main/resources/db/changelog/080-rbac-global.sql
+++ b/src/main/resources/db/changelog/080-rbac-global.sql
@@ -118,9 +118,32 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'ad
$$;
begin transaction;
-call defineContext('creating global admin role', null, null, null);
-select createRole(globalAdmin());
+ call defineContext('creating global admin role', null, null, null);
+ select createRole(globalAdmin());
commit;
+--//
+
+
+-- ============================================================================
+--changeset rbac-global-GUEST-ROLE:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+/*
+ A global guest role.
+ */
+create or replace function globalGuest(assumed boolean = true)
+ returns RbacRoleDescriptor
+ returns null on null input
+ stable -- leakproof
+ language sql as $$
+select 'global', (select uuid from RbacObject where objectTable = 'global'), 'guest'::RbacRoleType, assumed;
+$$;
+
+begin transaction;
+ call defineContext('creating global guest role', null, null, null);
+ select createRole(globalGuest());
+commit;
+--//
+
-- ============================================================================
--changeset rbac-global-ADMIN-USERS:1 context:dev,tc endDelimiter:--//
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.md b/src/main/resources/db/changelog/113-test-customer-rbac.md
index 7770e470..4d63eeac 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.md
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.md
@@ -1,6 +1,6 @@
### rbac customer
-This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.571772062.
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
@@ -21,6 +21,7 @@ subgraph customer["`**customer**`"]
subgraph customer:permissions[ ]
style customer:permissions fill:#dd4901,stroke:white
+ perm:customer:INSERT{{customer:INSERT}}
perm:customer:DELETE{{customer:DELETE}}
perm:customer:UPDATE{{customer:UPDATE}}
perm:customer:SELECT{{customer:SELECT}}
@@ -36,6 +37,7 @@ role:customer:owner ==> role:customer:admin
role:customer:admin ==> role:customer:tenant
%% granting permissions to roles
+role:global:admin ==> perm:customer:INSERT
role:customer:owner ==> perm:customer:DELETE
role:customer:admin ==> perm:customer:UPDATE
role:customer:tenant ==> perm:customer:SELECT
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/113-test-customer-rbac.sql
index 6ae19710..874cbc9a 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.sql
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql
@@ -1,5 +1,6 @@
--liquibase formatted sql
--- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
@@ -36,8 +37,8 @@ begin
perform createRoleWithGrants(
testCustomerOwner(NEW),
permissions => array['DELETE'],
- userUuids => array[currentUserUuid()],
- incomingSuperRoles => array[globalAdmin(unassumed())]
+ incomingSuperRoles => array[globalAdmin(unassumed())],
+ userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
@@ -72,15 +73,56 @@ create trigger insertTriggerForTestCustomer_tg
after insert on test_customer
for each row
execute procedure insertTriggerForTestCustomer_tf();
-
--//
+
-- ============================================================================
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
+/*
+ Creates INSERT INTO test_customer permissions for the related global rows.
+ */
+do language plpgsql $$
+ declare
+ row global;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO test_customer permissions for the related global rows');
+
+ FOR row IN SELECT * FROM global
+ LOOP
+ roleUuid := findRoleId(globalAdmin());
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer');
+ call grantPermissionToRole(permissionUuid, roleUuid);
+ END LOOP;
+ END;
+$$;
+
/**
- Checks if the user or assumed roles are allowed to insert a row to test_customer.
+ Adds test_customer INSERT permission to specified role of new global rows.
+*/
+create or replace function test_customer_global_insert_tf()
+ returns trigger
+ language plpgsql
+ strict as $$
+begin
+ call grantPermissionToRole(
+ createPermission(NEW.uuid, 'INSERT', 'test_customer'),
+ 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_test_customer_global_insert_tg
+ after insert on global
+ for each row
+execute procedure test_customer_global_insert_tf();
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to test_customer,
+ where only global-admin has that permission.
*/
create or replace function test_customer_insert_permission_missing_tf()
returns trigger
@@ -93,26 +135,27 @@ end; $$;
create trigger test_customer_insert_permission_check_tg
before insert on test_customer
for each row
- -- As there is no explicit INSERT grant specified for this table,
- -- only global admins are allowed to insert any rows.
when ( not isGlobalAdmin() )
execute procedure test_customer_insert_permission_missing_tf();
-
--//
+
-- ============================================================================
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('test_customer', $idName$
- prefix
+call generateRbacIdentityViewFromProjection('test_customer',
+ $idName$
+ prefix
$idName$);
-
--//
+
-- ============================================================================
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('test_customer',
- 'reference',
+ $orderBy$
+ reference
+ $orderBy$,
$updates$
reference = new.reference,
prefix = new.prefix,
@@ -120,4 +163,3 @@ call generateRbacRestrictedView('test_customer',
$updates$);
--//
-
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.md b/src/main/resources/db/changelog/123-test-package-rbac.md
index 78da4439..34b8c7c7 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.md
+++ b/src/main/resources/db/changelog/123-test-package-rbac.md
@@ -1,6 +1,6 @@
### rbac package
-This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.624847792.
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql
index 20562642..070d3fcc 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.sql
+++ b/src/main/resources/db/changelog/123-test-package-rbac.sql
@@ -1,5 +1,6 @@
--liquibase formatted sql
--- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
@@ -33,9 +34,10 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
- SELECT * FROM test_customer c
- WHERE c.uuid= NEW.customerUuid
- into newCustomer;
+
+ SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
+ assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
+
perform createRoleWithGrants(
testPackageOwner(NEW),
@@ -75,9 +77,9 @@ create trigger insertTriggerForTestPackage_tg
after insert on test_package
for each row
execute procedure insertTriggerForTestPackage_tf();
-
--//
+
-- ============================================================================
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@@ -99,17 +101,15 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
- SELECT * FROM test_customer c
- WHERE c.uuid= OLD.customerUuid
- into oldCustomer;
- SELECT * FROM test_customer c
- WHERE c.uuid= NEW.customerUuid
- into newCustomer;
+ SELECT * FROM test_customer WHERE uuid = OLD.customerUuid INTO oldCustomer;
+ assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
+
+ SELECT * FROM test_customer WHERE uuid = NEW.customerUuid INTO newCustomer;
+ assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
+
if NEW.customerUuid <> OLD.customerUuid then
- call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
-
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
@@ -138,9 +138,9 @@ create trigger updateTriggerForTestPackage_tg
after update on test_package
for each row
execute procedure updateTriggerForTestPackage_tf();
-
--//
+
-- ============================================================================
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@@ -160,7 +160,7 @@ do language plpgsql $$
LOOP
roleUuid := findRoleId(testCustomerAdmin(row));
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
- call grantPermissionToRole(roleUuid, permissionUuid);
+ call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
@@ -174,18 +174,22 @@ create or replace function test_package_test_customer_insert_tf()
strict as $$
begin
call grantPermissionToRole(
- testCustomerAdmin(NEW),
- createPermission(NEW.uuid, 'INSERT', 'test_package'));
+ createPermission(NEW.uuid, 'INSERT', 'test_package'),
+ testCustomerAdmin(NEW));
return NEW;
end; $$;
-create trigger test_package_test_customer_insert_tg
+-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
+create trigger z_test_package_test_customer_insert_tg
after insert on test_customer
for each row
execute procedure test_package_test_customer_insert_tf();
/**
- Checks if the user or assumed roles are allowed to insert a row to test_package.
+ Checks if the user or assumed roles are allowed to insert a row to test_package,
+ 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 test_package_insert_permission_missing_tf()
returns trigger
@@ -200,22 +204,25 @@ create trigger test_package_insert_permission_check_tg
for each row
when ( not hasInsertPermission(NEW.customerUuid, 'INSERT', 'test_package') )
execute procedure test_package_insert_permission_missing_tf();
-
--//
+
-- ============================================================================
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('test_package', $idName$
- name
+call generateRbacIdentityViewFromProjection('test_package',
+ $idName$
+ name
$idName$);
-
--//
+
-- ============================================================================
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('test_package',
- 'name',
+ $orderBy$
+ name
+ $orderBy$,
$updates$
version = new.version,
customerUuid = new.customerUuid,
@@ -223,4 +230,3 @@ call generateRbacRestrictedView('test_package',
$updates$);
--//
-
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.md b/src/main/resources/db/changelog/133-test-domain-rbac.md
index bd5cf706..6954e9b8 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.md
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.md
@@ -1,6 +1,6 @@
### rbac domain
-This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T11:29:11.644658132.
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
%%{init:{'flowchart':{'htmlLabels':false}}}%%
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql
index e686dada..bef72697 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.sql
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql
@@ -1,5 +1,6 @@
--liquibase formatted sql
--- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
@@ -33,9 +34,10 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
- SELECT * FROM test_package p
- WHERE p.uuid= NEW.packageUuid
- into newPackage;
+
+ SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
+ assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
+
perform createRoleWithGrants(
testDomainOwner(NEW),
@@ -71,9 +73,9 @@ create trigger insertTriggerForTestDomain_tg
after insert on test_domain
for each row
execute procedure insertTriggerForTestDomain_tf();
-
--//
+
-- ============================================================================
--changeset test-domain-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@@ -95,17 +97,15 @@ declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
- SELECT * FROM test_package p
- WHERE p.uuid= OLD.packageUuid
- into oldPackage;
- SELECT * FROM test_package p
- WHERE p.uuid= NEW.packageUuid
- into newPackage;
+ SELECT * FROM test_package WHERE uuid = OLD.packageUuid INTO oldPackage;
+ assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
+
+ SELECT * FROM test_package WHERE uuid = NEW.packageUuid INTO newPackage;
+ assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
+
if NEW.packageUuid <> OLD.packageUuid then
- call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testPackageAdmin(oldPackage));
-
call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
@@ -137,9 +137,9 @@ create trigger updateTriggerForTestDomain_tg
after update on test_domain
for each row
execute procedure updateTriggerForTestDomain_tf();
-
--//
+
-- ============================================================================
--changeset test-domain-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
@@ -159,7 +159,7 @@ do language plpgsql $$
LOOP
roleUuid := findRoleId(testPackageAdmin(row));
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
- call grantPermissionToRole(roleUuid, permissionUuid);
+ call grantPermissionToRole(permissionUuid, roleUuid);
END LOOP;
END;
$$;
@@ -173,18 +173,22 @@ create or replace function test_domain_test_package_insert_tf()
strict as $$
begin
call grantPermissionToRole(
- testPackageAdmin(NEW),
- createPermission(NEW.uuid, 'INSERT', 'test_domain'));
+ createPermission(NEW.uuid, 'INSERT', 'test_domain'),
+ testPackageAdmin(NEW));
return NEW;
end; $$;
-create trigger test_domain_test_package_insert_tg
+-- z_... is to put it at the end of after insert triggers, to make sure the roles exist
+create trigger z_test_domain_test_package_insert_tg
after insert on test_package
for each row
execute procedure test_domain_test_package_insert_tf();
/**
- Checks if the user or assumed roles are allowed to insert a row to test_domain.
+ Checks if the user or assumed roles are allowed to insert a row to test_domain,
+ 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 test_domain_insert_permission_missing_tf()
returns trigger
@@ -199,22 +203,25 @@ create trigger test_domain_insert_permission_check_tg
for each row
when ( not hasInsertPermission(NEW.packageUuid, 'INSERT', 'test_domain') )
execute procedure test_domain_insert_permission_missing_tf();
-
--//
+
-- ============================================================================
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('test_domain', $idName$
- name
+call generateRbacIdentityViewFromProjection('test_domain',
+ $idName$
+ name
$idName$);
-
--//
+
-- ============================================================================
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('test_domain',
- 'name',
+ $orderBy$
+ name
+ $orderBy$,
$updates$
version = new.version,
packageUuid = new.packageUuid,
@@ -222,4 +229,3 @@ call generateRbacRestrictedView('test_domain',
$updates$);
--//
-
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md
new file mode 100644
index 00000000..f3547312
--- /dev/null
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md
@@ -0,0 +1,43 @@
+### 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}}
+ 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
+
+```
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
new file mode 100644
index 00000000..136dad87
--- /dev/null
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
@@ -0,0 +1,126 @@
+--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:--//
+-- ----------------------------------------------------------------------------
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to hs_office_contact,
+ where only global-admin has that permission.
+*/
+create or replace function hs_office_contact_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger hs_office_contact_insert_permission_check_tg
+ before insert on hs_office_contact
+ for each row
+ when ( not isGlobalAdmin() )
+ execute procedure hs_office_contact_insert_permission_missing_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$);
+--//
+
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md
new file mode 100644
index 00000000..aa971642
--- /dev/null
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md
@@ -0,0 +1,43 @@
+### 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: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:person:owner ==> perm:person:DELETE
+role:person:admin ==> perm:person:UPDATE
+role:person:referrer ==> perm:person:SELECT
+
+```
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
new file mode 100644
index 00000000..f99c2a46
--- /dev/null
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
@@ -0,0 +1,126 @@
+--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:--//
+-- ----------------------------------------------------------------------------
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to hs_office_person,
+ where only global-admin has that permission.
+*/
+create or replace function hs_office_person_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into hs_office_person not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger hs_office_person_insert_permission_check_tg
+ before insert on hs_office_person
+ for each row
+ when ( not isGlobalAdmin() )
+ execute procedure hs_office_person_insert_permission_missing_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$);
+--//
+
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
new file mode 100644
index 00000000..14f797eb
--- /dev/null
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
@@ -0,0 +1,100 @@
+### 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}}
+ 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
+
+```
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
new file mode 100644
index 00000000..5301dc56
--- /dev/null
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
@@ -0,0 +1,191 @@
+--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;
+
+ SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
+
+ SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
+
+ 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 $$
+begin
+
+ if NEW.contactUuid is distinct from OLD.contactUuid then
+ delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
+ call buildRbacSystemForHsOfficeRelation(NEW);
+ end if;
+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:--//
+-- ----------------------------------------------------------------------------
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
+ where only global-admin has that permission.
+*/
+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 isGlobalAdmin() )
+ 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$);
+--//
+
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
new file mode 100644
index 00000000..98bd276d
--- /dev/null
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
@@ -0,0 +1,158 @@
+### 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.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 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
+
+ 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
+
+```
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
new file mode 100644
index 00000000..8b12e95f
--- /dev/null
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
@@ -0,0 +1,248 @@
+--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;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows');
+
+ FOR row IN SELECT * FROM global
+ LOOP
+ roleUuid := findRoleId(globalAdmin());
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner');
+ call grantPermissionToRole(permissionUuid, roleUuid);
+ 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 generateRbacIdentityViewFromQuery('hs_office_partner',
+ $idName$
+ SELECT partner.partnerNumber
+ || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
+ || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
+ FROM hs_office_partner AS partner
+ $idName$);
+--//
+
+-- ============================================================================
+--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('hs_office_partner',
+ $orderBy$
+ SELECT partner.partnerNumber
+ || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
+ || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
+ FROM hs_office_partner AS partner
+ $orderBy$,
+ $updates$
+ partnerRelUuid = new.partnerRelUuid,
+ personUuid = new.personUuid,
+ contactUuid = new.contactUuid
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
new file mode 100644
index 00000000..ece32f9c
--- /dev/null
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
@@ -0,0 +1,136 @@
+### rbac partnerDetails
+
+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 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
+
+ subgraph partnerRel["`**partnerRel**`"]
+ direction TB
+ style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+ 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 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
+
+ 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 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:partnerDetails:INSERT
+
+```
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
new file mode 100644
index 00000000..4fd78a87
--- /dev/null
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
@@ -0,0 +1,164 @@
+--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
+ newPartnerRel hs_office_relation;
+
+begin
+ call enterTriggerForObjectUuid(NEW.uuid);
+
+ SELECT partnerRel.*
+ FROM hs_office_relation AS partnerRel
+ JOIN hs_office_partner AS partner
+ ON partner.detailsUuid = NEW.uuid
+ WHERE partnerRel.uuid = partner.partnerRelUuid
+ INTO newPartnerRel;
+ assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
+
+ 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;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows');
+
+ FOR row IN SELECT * FROM global
+ LOOP
+ roleUuid := findRoleId(globalAdmin());
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner_details');
+ call grantPermissionToRole(permissionUuid, roleUuid);
+ 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 partner_iv.idName || '-details'
+ 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$
+ SELECT partner_iv.idName || '-details'
+ 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
+ $orderBy$,
+ $updates$
+ registrationOffice = new.registrationOffice,
+ registrationNumber = new.registrationNumber,
+ birthPlace = new.birthPlace,
+ birthName = new.birthName,
+ birthday = new.birthday,
+ dateOfDeath = new.dateOfDeath
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
new file mode 100644
index 00000000..4f1604fb
--- /dev/null
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
@@ -0,0 +1,43 @@
+### 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: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:bankAccount:owner ==> perm:bankAccount:DELETE
+role:bankAccount:admin ==> perm:bankAccount:UPDATE
+role:bankAccount:referrer ==> perm:bankAccount:SELECT
+
+```
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
new file mode 100644
index 00000000..6b96fb34
--- /dev/null
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
@@ -0,0 +1,125 @@
+--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:--//
+-- ----------------------------------------------------------------------------
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount,
+ where only global-admin has that permission.
+*/
+create or replace function hs_office_bankaccount_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger hs_office_bankaccount_insert_permission_check_tg
+ before insert on hs_office_bankaccount
+ for each row
+ when ( not isGlobalAdmin() )
+ execute procedure hs_office_bankaccount_insert_permission_missing_tf();
+--//
+
+-- ============================================================================
+--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
+ $idName$
+ iban || ':' || holder
+ $idName$);
+--//
+
+-- ============================================================================
+--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('hs_office_bankaccount',
+ $orderBy$
+ iban || ':' || holder
+ $orderBy$,
+ $updates$
+ holder = new.holder,
+ iban = new.iban,
+ bic = new.bic
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
new file mode 100644
index 00000000..f542e78c
--- /dev/null
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
@@ -0,0 +1,178 @@
+### 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}}
+ end
+end
+
+subgraph debitorRel["`**debitorRel**`"]
+ direction TB
+ style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ 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 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
+
+```
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
new file mode 100644
index 00000000..1e383951
--- /dev/null
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
@@ -0,0 +1,143 @@
+--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;
+
+ SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
+
+ 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:--//
+-- ----------------------------------------------------------------------------
+
+/**
+ Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
+ where only global-admin has that permission.
+*/
+create or replace function hs_office_sepamandate_insert_permission_missing_tf()
+ returns trigger
+ language plpgsql as $$
+begin
+ raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
+ currentSubjects(), currentSubjectsUuids();
+end; $$;
+
+create trigger hs_office_sepamandate_insert_permission_check_tg
+ before insert on hs_office_sepamandate
+ for each row
+ when ( not isGlobalAdmin() )
+ execute procedure hs_office_sepamandate_insert_permission_missing_tf();
+--//
+
+-- ============================================================================
+--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+
+call generateRbacIdentityViewFromProjection('hs_office_sepamandate',
+ $idName$
+ concat(tradeName, familyName, givenName)
+ $idName$);
+--//
+
+-- ============================================================================
+--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('hs_office_sepamandate',
+ $orderBy$
+ concat(tradeName, familyName, givenName)
+ $orderBy$,
+ $updates$
+ reference = new.reference,
+ agreement = new.agreement,
+ validity = new.validity
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
new file mode 100644
index 00000000..a1baa702
--- /dev/null
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
@@ -0,0 +1,275 @@
+### 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.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 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: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.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 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 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: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
+
+```
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
new file mode 100644
index 00000000..f827ea67
--- /dev/null
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
@@ -0,0 +1,231 @@
+--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 * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
+
+ 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.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;
+ permissionUuid uuid;
+ roleUuid uuid;
+ begin
+ call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows');
+
+ FOR row IN SELECT * FROM global
+ LOOP
+ roleUuid := findRoleId(globalAdmin());
+ permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor');
+ call grantPermissionToRole(permissionUuid, roleUuid);
+ 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,
+ '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 partnerRel.type = 'DEBITOR'
+ WHERE debitorRel.uuid = debitor.debitorRelUuid)
+ || to_char(debitorNumberSuffix, 'fm00')
+ from hs_office_debitor as debitor
+ $idName$);
+--//
+
+-- ============================================================================
+--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
+-- ----------------------------------------------------------------------------
+call generateRbacRestrictedView('hs_office_debitor',
+ $orderBy$
+ SELECT debitor.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 partnerRel.type = 'DEBITOR'
+ WHERE debitorRel.uuid = debitor.debitorRelUuid)
+ || to_char(debitorNumberSuffix, 'fm00')
+ from hs_office_debitor as debitor
+ $orderBy$,
+ $updates$
+ debitorRel = new.debitorRel,
+ billable = new.billable,
+ debitorUuid = new.debitorUuid,
+ refundBankAccountUuid = new.refundBankAccountUuid,
+ vatId = new.vatId,
+ vatCountryCode = new.vatCountryCode,
+ vatBusiness = new.vatBusiness,
+ vatReverseCharge = new.vatReverseCharge,
+ defaultPrefix = new.defaultPrefix
+ $updates$);
+--//
+
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 5934c9a4..6047befa 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -11,6 +11,8 @@ databaseChangeLog:
file: db/changelog/005-uuid-ossp-extension.sql
- include:
file: db/changelog/006-numeric-hash-functions.sql
+ - include:
+ file: db/changelog/007-table-columns.sql
- include:
file: db/changelog/009-check-environment.sql
- include:
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
index 293741b6..7574d8b2 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
@@ -462,7 +462,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_partner#FirstGmbH-firstcontact.agent")
+ .header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.admin")
.port(port)
.when()
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
index 942351c0..e9e1d47c 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
@@ -204,7 +204,7 @@ class TestCustomerControllerAcceptanceTest {
.statusCode(403)
.contentType(ContentType.JSON)
.statusCode(403)
- .body("message", containsString("insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
+ .body("message", containsString("ERROR: [403] insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
// @formatter:on
// finally, the new customer was not created
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
index eca0aec1..d576396a 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
@@ -29,6 +29,7 @@ class TestCustomerEntityUnitTest {
subgraph customer:permissions[ ]
style customer:permissions fill:#dd4901,stroke:white
+ perm:customer:INSERT{{customer:INSERT}}
perm:customer:DELETE{{customer:DELETE}}
perm:customer:UPDATE{{customer:UPDATE}}
perm:customer:SELECT{{customer:SELECT}}
@@ -44,6 +45,7 @@ class TestCustomerEntityUnitTest {
role:customer:admin ==> role:customer:tenant
%% granting permissions to roles
+ role:global:admin ==> perm:customer:INSERT
role:customer:owner ==> perm:customer:DELETE
role:customer:admin ==> perm:customer:UPDATE
role:customer:tenant ==> perm:customer:SELECT
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index a4f570f9..40ae85bb 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -4,8 +4,9 @@ spring:
platform: postgres
datasource:
- url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
+ url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
url-local: jdbc:postgresql://localhost:5432/postgres
+ url: ${spring.datasource.url-tc}
username: postgres
password: password
From d3ca2b7e234105f354b87d965ebff96cc45ea2ab Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Thu, 28 Mar 2024 12:15:13 +0100
Subject: [PATCH 06/12] move Parter+Debitor person+contact to related
Relationsship (#20)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/20
Reviewed-by: Timotheus Pokorra
---
doc/ideas/rbac-schema-f.md | 82 +++
doc/ideas/simplified-grant-structure.md | 29 +
doc/rbac.md | 2 +-
.../HsOfficeBankAccountEntity.java | 9 +-
.../office/contact/HsOfficeContactEntity.java | 5 +-
.../HsOfficeCoopAssetsTransactionEntity.java | 10 +-
.../HsOfficeCoopSharesTransactionEntity.java | 1 -
.../debitor/HsOfficeDebitorController.java | 47 +-
.../office/debitor/HsOfficeDebitorEntity.java | 96 ++--
.../debitor/HsOfficeDebitorEntityPatcher.java | 8 +-
.../debitor/HsOfficeDebitorRepository.java | 17 +-
.../HsOfficeMembershipController.java | 7 +-
.../membership/HsOfficeMembershipEntity.java | 64 ++-
.../HsOfficeMembershipEntityPatcher.java | 17 -
.../partner/HsOfficePartnerController.java | 6 +-
.../partner/HsOfficePartnerDetailsEntity.java | 21 +-
.../office/partner/HsOfficePartnerEntity.java | 69 +--
.../partner/HsOfficePartnerEntityPatcher.java | 16 +-
.../partner/HsOfficePartnerRepository.java | 7 +-
.../office/person/HsOfficePersonEntity.java | 4 +-
.../relation/HsOfficeRelationEntity.java | 18 +-
.../HsOfficeSepaMandateController.java | 1 +
.../HsOfficeSepaMandateEntity.java | 30 +-
.../rbac/rbacdef/InsertTriggerGenerator.java | 8 +-
.../hsadminng/stringify/Stringify.java | 10 +-
.../hs-office/hs-office-debitor-schemas.yaml | 19 +-
.../hs-office-membership-schemas.yaml | 9 -
.../hs-office/hs-office-partner-schemas.yaml | 21 +-
...s.yaml => hs-office-relation-schemas.yaml} | 0
.../hs-office-relations-with-uuid.yaml | 6 +-
.../hs-office/hs-office-relations.yaml | 8 +-
.../rbac/rbac-role-schemas.yaml | 1 +
.../changelog/006-numeric-hash-functions.sql | 2 +-
.../resources/db/changelog/010-context.sql | 31 +-
.../resources/db/changelog/020-audit-log.sql | 4 +-
.../resources/db/changelog/055-rbac-views.sql | 17 +-
.../db/changelog/113-test-customer-rbac.sql | 8 +-
.../db/changelog/123-test-package-rbac.sql | 8 +-
.../db/changelog/133-test-domain-rbac.sql | 8 +-
.../203-hs-office-contact-rbac-generated.sql | 126 -----
...rated.md => 203-hs-office-contact-rbac.md} | 2 +
.../changelog/203-hs-office-contact-rbac.sql | 185 +++----
.../db/changelog/210-hs-office-person.sql | 1 -
.../213-hs-office-person-rbac-generated.sql | 126 -----
...erated.md => 213-hs-office-person-rbac.md} | 2 +
.../changelog/213-hs-office-person-rbac.sql | 189 +++----
.../218-hs-office-person-test-data.sql | 5 +-
.../223-hs-office-relation-rbac-generated.md | 100 ----
.../223-hs-office-relation-rbac-generated.sql | 191 -------
.../changelog/223-hs-office-relation-rbac.md | 120 +++--
.../changelog/223-hs-office-relation-rbac.sql | 347 +++++++-----
.../228-hs-office-relation-test-data.sql | 25 +-
.../db/changelog/230-hs-office-partner.sql | 22 +-
.../233-hs-office-partner-rbac-generated.md | 158 ------
.../233-hs-office-partner-rbac-generated.sql | 248 ---------
.../changelog/233-hs-office-partner-rbac.md | 204 ++++---
.../changelog/233-hs-office-partner-rbac.sql | 388 +++++++-------
...s-office-partner-details-rbac-generated.md | 136 -----
...-office-partner-details-rbac-generated.sql | 164 ------
.../234-hs-office-partner-details-rbac.md | 23 +
.../234-hs-office-partner-details-rbac.sql | 185 +++++--
.../238-hs-office-partner-test-data.sql | 17 +-
...43-hs-office-bankaccount-rbac-generated.md | 43 --
...3-hs-office-bankaccount-rbac-generated.sql | 125 -----
.../243-hs-office-bankaccount-rbac.md | 69 +--
.../243-hs-office-bankaccount-rbac.sql | 184 +++----
...53-hs-office-sepamandate-rbac-generated.md | 178 -------
...3-hs-office-sepamandate-rbac-generated.sql | 143 -----
.../253-hs-office-sepamandate-rbac.md | 221 ++++++--
.../253-hs-office-sepamandate-rbac.sql | 251 +++++----
.../258-hs-office-sepamandate-test-data.sql | 33 +-
.../db/changelog/270-hs-office-debitor.sql | 39 +-
.../273-hs-office-debitor-rbac-generated.md | 275 ----------
.../273-hs-office-debitor-rbac-generated.sql | 231 --------
.../changelog/273-hs-office-debitor-rbac.md | 503 +++++++++---------
.../changelog/273-hs-office-debitor-rbac.sql | 358 ++++++-------
.../278-hs-office-debitor-test-data.sql | 39 +-
.../db/changelog/300-hs-office-membership.sql | 1 -
.../303-hs-office-membership-rbac.md | 204 ++++---
.../303-hs-office-membership-rbac.sql | 234 ++++----
.../308-hs-office-membership-test-data.sql | 30 +-
.../313-hs-office-coopshares-rbac.sql | 2 +-
.../323-hs-office-coopassets-rbac.sql | 2 +-
.../hsadminng/arch/ArchitectureTest.java | 33 +-
.../HsOfficeBankAccountEntityUnitTest.java | 2 +-
...eBankAccountRepositoryIntegrationTest.java | 26 +-
...fficeContactRepositoryIntegrationTest.java | 21 +-
...tsTransactionControllerAcceptanceTest.java | 4 +-
...ceCoopAssetsTransactionEntityUnitTest.java | 8 +-
...sTransactionRepositoryIntegrationTest.java | 38 +-
...esTransactionControllerAcceptanceTest.java | 24 +-
...sTransactionRepositoryIntegrationTest.java | 6 +-
...OfficeDebitorControllerAcceptanceTest.java | 462 +++++++++++-----
.../HsOfficeDebitorEntityPatcherUnitTest.java | 49 +-
.../HsOfficeDebitorEntityUnitTest.java | 57 +-
...fficeDebitorRepositoryIntegrationTest.java | 257 +++++----
.../office/debitor/TestHsOfficeDebitor.java | 9 +-
...iceMembershipControllerAcceptanceTest.java | 129 ++---
.../HsOfficeMembershipControllerRestTest.java | 69 +--
...OfficeMembershipEntityPatcherUnitTest.java | 18 +-
.../HsOfficeMembershipEntityUnitTest.java | 4 +-
...ceMembershipRepositoryIntegrationTest.java | 112 ++--
.../hs/office/migration/ImportOfficeData.java | 249 +++++----
...OfficePartnerControllerAcceptanceTest.java | 129 +++--
.../HsOfficePartnerControllerRestTest.java | 26 -
.../HsOfficePartnerEntityPatcherUnitTest.java | 56 +-
.../HsOfficePartnerEntityUnitTest.java | 48 +-
...fficePartnerRepositoryIntegrationTest.java | 228 ++++----
.../office/partner/TestHsOfficePartner.java | 26 +-
...sOfficePersonControllerAcceptanceTest.java | 2 +-
...OfficePersonRepositoryIntegrationTest.java | 21 +-
...fficeRelationControllerAcceptanceTest.java | 2 +-
...ficeRelationRepositoryIntegrationTest.java | 89 ++--
...ceSepaMandateControllerAcceptanceTest.java | 77 ++-
...eSepaMandateRepositoryIntegrationTest.java | 93 ++--
.../test/ContextBasedTestWithCleanup.java | 38 ++
.../hsadminng/hs/office/test/EntityList.java | 15 +
117 files changed, 3995 insertions(+), 5287 deletions(-)
create mode 100644 doc/ideas/rbac-schema-f.md
create mode 100644 doc/ideas/simplified-grant-structure.md
rename src/main/resources/api-definition/hs-office/{hs-office-relations-schemas.yaml => hs-office-relation-schemas.yaml} (100%)
delete mode 100644 src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
rename src/main/resources/db/changelog/{203-hs-office-contact-rbac-generated.md => 203-hs-office-contact-rbac.md} (92%)
delete mode 100644 src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
rename src/main/resources/db/changelog/{213-hs-office-person-rbac-generated.md => 213-hs-office-person-rbac.md} (92%)
delete mode 100644 src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
delete mode 100644 src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
delete mode 100644 src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
create mode 100644 src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
delete mode 100644 src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
delete mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
delete mode 100644 src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
delete mode 100644 src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
diff --git a/doc/ideas/rbac-schema-f.md b/doc/ideas/rbac-schema-f.md
new file mode 100644
index 00000000..7047d066
--- /dev/null
+++ b/doc/ideas/rbac-schema-f.md
@@ -0,0 +1,82 @@
+*(this is just a scribbled draft, that's why it's still in German)*
+
+### *Schema-F* für Permissions, Rollen und Grants
+
+Permissions, Rollen und Grants werden in den INSERT/UPDATE/DELETE-Triggern von Geschäftsobjekten erzeugt und gelöscht. Das Löschen erfolgt meistens automatisch über das zugehörige RbacObject, die INSERT- und UPDATE-Trigger müssen jedoch in *pl/pgsql* ausprogrammiert werden.
+
+Das folgende Schema soll dabei unterstützen, die richtigen Permissions, Rollen und Grants festzulegen.
+
+An einigen Stellen ist vom *Initiator* die Rede. Als *Initiator* gilt derjenige User, der die Operation (INSERT oder UPDATE) durchführt bzw. dessen primary assumed Rol. (TODO: bisher gibt es nur assumed roles, das Konzept einer primary assumed Role müsste noch eingeführt werden, derzeit nehmen wir dafür immer den `globalAdmin()`. Bevor Kunden aber selbst Objekte anlegen können, muss das geklärt sein.)
+
+#### Typ Root: Objekte, welche nur eine Spezialisierung bzw. Zusatzdaten für andere Objekte bereitstellen (z.B. Partner für Relations vom Typ Partner oder Partner Details für Partner)
+
+Objektorientiert gedacht, enthalten solche Objekte die Zusatzdaten einer Subklasse; die Daten im Partner erweitern also eine Relation vom Typ `partner`.
+
+- Dann muss dieses Objekt zeitlich nach dem Objekt erzeugt werden, auf dass es sich bezieht, also z.B. zeitlich nach der Relation.
+- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
+- Es werden **keine** Rollen für dieses Objekt erzeugt.
+- Statt eigener Rollen werden die o.g. Permissions passenden Rollen des Hauptobjekts zugewiesen (granted) bzw. aus denen entfernt (revoked).
+ - Handelt es sich um Zusatzdaten zum Zwecke der Spezialisierung, dann z.B. so:
+ - Delete (\*) <-- Owner des Hauptobjektes
+ - Edit <-- **Admin** des Hauptobjektes
+ - View <-- Agent des Hauptobjektes
+ - Handelt es sich um Zusatzdaten, für die sich Edit-Rechte delegieren lassen sollen (wie im Falle der Partner-Details eines Partners), dann z.B. so:
+ - Delete (\*) <-- Owner des Hauptobjektes
+ - Edit <-- **Agent** des Hauptobjektes
+ - View <-- Agent des Hauptobjektes
+- Für die Rollenzuordnung zwischen referenzierten Objekten gilt:
+ - Für Objekte vom Typ Root werden die Rollen des zugehörigen Aggregator-Objektes verwendet.
+ - Gibt es Referenzen auf hierarchisch verbundene Objekte (z.B. Debitor.refundBankAccount) gilt folgende Faustregel:
+ ***Nach oben absteigen, nach unten halten oder aufsteigen.*** An einem fachlich übergeordneten Objekt wird also eine niedrigere Rolle (z.B. Debitor-admin -> Partner.agent), einem fachlich untergeordneten Objekt eine gleichwertige Rolle (z.B. Partner.admin -> Debitor.admin) zugewiesen oder sogar aufgestiegen (Debitor.admin -> Package.tenant).
+ - Für Referenzen zwischen Objekten, die nicht hierarchisch zueinander stehen (z.B. Debitor und Bankverbindung), wird auf beiden seiten abgestiegen (also Debitor.admin -> BankAccount.referrer und BankAccount.admin -> Debitor.tenant).
+
+Anmerkung: Der Typ-Begriff *Root* bezieht sich auf die Rolle im fachlichen Datenmodell. Im Bezug auf den Teilgraphen eines fachlichen Kontexts ist dies auch eine Wurzel im Sinne der Graphentheorie. Aber in anderen fachlichen Kontexten können auch diese Objekte von anderen Teilgraphen referenziert werden und werden dann zum inneren Knoten.
+
+
+#### Typ Aggregator: Objekte, welche weitere Objekte zusammenfassen (z.B. Relation fasst zwei Persons und einen Contact zusammen)
+
+Solche Objekte verweisen üblicherweise auf Objekte vom Typ Leaf und werden oft von Objekten des Typs Root referenziert.
+
+- Es werden i.d.R. folgende Rollen für diese Objekte erzeugt:
+ - Owner, Admin, Agent, Tenent(, Guest?)
+- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
+- Die Permissions werden den Rollen sinnvoll zugewiesen, z.B.:
+ - Owner -> Delete (\*)
+ - Admin --> Edit
+ - Tenant (oder ggf. Guest) --> View
+- Außerdem werden folgende Grants erstellt bzw. entzogen:
+ - Initiator --> Owner
+ - Owner --> Admin
+ - Admin --> Referrer
+ - Admins der referenzierten Objekte werden Agent des Aggregators
+ - Tenants des Aggregators werden Referrer der referenzierten Objekte
+
+### Typ Leaf: Handelt es sich um ein Objekt, welches (außer zur Modellierung separater Permissions) keine Unterobjekte enthält (z.B. Person, Customer)?
+
+Solche Objekte werden üblicherweise von Objekten des Typs Aggregator, manchmal auch von Objekten des Typs Root, referenziert.
+
+- Es werden i.d.R. folgende Rollen für diese Objekte erzeugt:
+ - Owner, Admin, Referrer
+- Es werden Delete (\*), Edit und View Permissions für dieses Objekt erzeugt.
+- Die Permissions werden den Rollen sinnvoll zugewiesen, z.B.:
+ - Delete (\*) <-- Owner
+ - Edit <-- Admin
+ - View <-- Referrer
+- Außerdem werden folgende Grants erstellt bzw. entzogen:
+ - Owner --> Admin
+ - Admin --> Referrer
+
+```mermaid
+flowchart LR
+
+subgraph partnerDetails
+ direction TB
+ style partnerDetails fill:#eee
+
+ perm:partnerDetails.*{{partnerDetails.*}}
+ role:partnerDetails.edit{{partnerDetails.edit}}
+ role:partnerDetails.view{{partnerDetails.view}}
+
+
+end
+```
diff --git a/doc/ideas/simplified-grant-structure.md b/doc/ideas/simplified-grant-structure.md
new file mode 100644
index 00000000..6d89897a
--- /dev/null
+++ b/doc/ideas/simplified-grant-structure.md
@@ -0,0 +1,29 @@
+(this is just a scribbled idea, that's why it's still in German)
+
+Ich habe mal wieder vom RBAC-System geträumt 🙈 Ok, im Halbschlaf darüber nachgedacht trifft es wohl besser. Und jetzt frage ich mich, ob wir viel zu kompliziert gedacht haben.
+
+Bislang gingen wir ja davon aus, dass, wenn komplexe Entitäten (z.B. Partner) erzeugt werden, wir wir über den INSERT-Trigger den Rollen der verknüpften Entitäten (z.B. den Rollen der Personendaten des Partners) auch Rechte an den komplexeren Entitäten und umgekehrt geben müssen.
+
+Da die komplexen Entitäten nur mit gewissen verbundenen Entitäten überhaupt sinnvoll nutzbar sind und diese daher über INNSER JOINs mitladen, könnte sonst auch nur jemand diese Entitäten, der auch die SELECT-Permission an den verküpften Entitäten hat.
+
+Vor einigen Wochen hatten wir schon einmal darüber geredet, ob wir dieses Geflecht wirklich komplett durchplanen müssen, also über mehrere Stufen hinweg, oder ob sehr warscheinlich eh dieselben Leuten an den weiter entfernten Entitäten die nötien Rechte haben, weil dahinter dieselben User stehen. Also z.B. dass gewährleistet ist, dass jemand mit ADMIN-Recht an den Personendaten des Partners auch bis in die SEPA-Mandate eines Debitors hineinsehen kann.
+
+Und nun gehe ich noch einen Schritt weiter: Könnte es nicht auch andersherum sein? Also wenn jemand z.B. SELECT-Recht am Partner hat, dass wir davon ausgehen können, dass derjenige auch die Partner-Personen- und Kontaktdaten sehen darf, und zwar implizit durch seine Partner-SELECT-Permission und ohne dass er explizit Rollen für diese Partner-Personen oder Kontaktdaten inne hat?
+
+Im Halbschlaf kam mir nur die Idee, warum wir nicht einfach die komplexen JPA-Entitäten zwar auf die restricted View setzen, wie bisher, aber für die verknüpften Entitäten auf die direkten (bisher "Raw..." genannt) Entitäten gehen. Dann könnte jemand mit einer Rolle, welche die SELECT-Permission auf die komplexe JPA-Entität (z.B.) Partner inne hat, auch die dazugehörige Relation(ship) ["Relation" wurde vor kurzem auf kurz "Relation" umbenannt] und die wiederum dazu gehörigen Personen- und Kontaktdaten lesen, ohne dass in einem INSERT- und UPDATE-Trigger der Partner-Entität die ganzen Grants mit den verknüpften Entäten aufgebaut und aktualisiert werden müssen.
+
+Beim Debitor ist das nämlich selbst mit Generator die Hölle, zumal eben auch Querverbindungen gegranted werden müssen, z.B. von der Debitor-Person zum Sema-Mandat - jedenfalls wenn man nicht Gefahr laufen wollte, dass jemand mit Admin-Rechten an der Partner-Person (also z.B. ein Repräsentant des Partners) die Sepa-Mandate der Debitoren gar nicht mehr sehen kann. Natürlich bräuchte man immer noch die Agent-Rolle am Partner und Debitor (evtl. repräsentiert durch die jeweils zugehörigen Relation - falls dieser Trick überhaupt noch nötig wäre), sowie ein Grant vom Partner-Agent auf den Debitor-Agent und vom Debitor-Agent auf die Sepa-Mandate-Admins, aber eben ohne filigran die ganzen Neben-Entäten (Personen- und Kontaktdaten von Partner und Debitor sowie Bank-Account) in jedem Trigger berücksichtigen zu müssen. Beim Refund-Bank-Account sogar besonders ätzend, weil der optional ist und dadurch zig "if ...refundBankAccountUuid is not null then ..." im Code enstehen (wenn der auch generiert ist).
+
+Mit anderen Worten, um als Repräsentant eines Geschäftspartners auf den Bank-Account der Sepa-Mandate sehen zu dürfen, wird derzeut folgende Grant-Kette durchlaufen (bzw. eben noch nicht, weil es noch nicht funktioniert):
+
+User -> Partner-Holder-Person:Admin -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> BankAccount:Admin -> BankAccount:SELECT
+
+Daraus würde:
+
+User -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> Sepa-Mandat:SELECT*
+
+(*mit JOIN auf RawBankAccount, also implizitem Leserecht)
+
+Das klingt zunächst nach nur einer marginalen Vereinfachung, die eigentlich Vereinfachung liegt aber im Erzeugen der Grants in den Triggern, denn da sind zudem noch Partner-Anchor-Person, Debitor-Holder- und Anchor-Person, Partner- und Debitor-Contact sowie der RefundBankAccount zu berücksichtigen. Und genau diese Grants würden großteils wegfallen, und durch implizite Persmissions über die JOINs auf die Raw-Tables ersetzt werden. Den refundBankAccound müssten wir dann, analog zu den Sepa-Mandataten, umgedreht modellieren, da den sonst
+
+Man könnte das Ganze auch als "Entwicklung der Rechtestruktur für Hosting-Entitäten auf der obersten Ebene" (Manged Webspace, Managed Server, Cloud Server etc.) sehen, denn die hängen alle unter dem Mega-komplexen Debitor.
diff --git a/doc/rbac.md b/doc/rbac.md
index 9aa4b024..2de4d4bb 100644
--- a/doc/rbac.md
+++ b/doc/rbac.md
@@ -1,6 +1,6 @@
## *hsadmin-ng*'s Role-Based-Access-Management (RBAC)
-The requirements of *hsadmin-ng* include table-m row- and column-level-security for read and write access to business-objects.
+The requirements of *hsadmin-ng* include table-, row- and column-level-security for read and write access to business-objects.
More precisely, any access has to be controlled according to given rules depending on the accessing users, their roles and the accessed business-object.
Further, roles and business-objects are hierarchical.
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
index de256ca1..664ed8fe 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
@@ -33,8 +33,8 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
private static Stringify toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
+ .withIdProp(HsOfficeBankAccountEntity::getIban)
.withProp(Fields.holder, HsOfficeBankAccountEntity::getHolder)
- .withProp(Fields.iban, HsOfficeBankAccountEntity::getIban)
.withProp(Fields.bic, HsOfficeBankAccountEntity::getBic);
@Id
@@ -59,8 +59,11 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
public static RbacView rbac() {
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
- .withIdentityView(SQL.projection("iban || ':' || holder"))
+ .withIdentityView(SQL.projection("iban"))
.withUpdatableColumns("holder", "iban", "bic")
+
+ .toRole("global", GUEST).grantPermission(INSERT)
+
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
@@ -75,6 +78,6 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
+ rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
index 406b232c..62f5316a 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
@@ -75,10 +75,11 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
})
.createSubRole(REFERRER, (with) -> {
with.permission(SELECT);
- });
+ })
+ .toRole(GLOBAL, GUEST).grantPermission(INSERT);
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("203-hs-office-contact-rbac-generated");
+ rbac().generateWithBaseFileName("203-hs-office-contact-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
index 2c6fdb1b..0b579a85 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
@@ -12,6 +12,7 @@ import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.util.Optional;
import java.util.UUID;
import static java.util.Optional.ofNullable;
@@ -28,13 +29,12 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
private static Stringify stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
- .withProp(HsOfficeCoopAssetsTransactionEntity::getMemberNumber)
+ .withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
.withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate)
.withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType)
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
- .withSeparator(", ")
.quotedValues(false);
@Id
@@ -76,8 +76,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
private String comment;
- public Integer getMemberNumber() {
- return ofNullable(membership).map(HsOfficeMembershipEntity::getMemberNumber).orElse(null);
+ public String getTaggedMemberNumber() {
+ return ofNullable(membership).map(HsOfficeMembershipEntity::toShortString).orElse("M-?????");
}
@Override
@@ -87,6 +87,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
@Override
public String toShortString() {
- return "%s%+1.2f".formatted(getMemberNumber(), assetValue);
+ return "%s:%+1.2f".formatted(getTaggedMemberNumber(), Optional.ofNullable(assetValue).orElse(BigDecimal.ZERO));
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
index c7ba9527..807af25f 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
@@ -31,7 +31,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
- .withSeparator(", ")
.quotedValues(false);
@Id
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java
index bc4175ca..5455b99b 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorController.java
@@ -5,7 +5,11 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitors
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
+import org.apache.commons.lang3.Validate;
+import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
@@ -13,10 +17,13 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import jakarta.persistence.EntityManager;
+import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import java.util.List;
import java.util.UUID;
+import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
+
@RestController
public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@@ -30,6 +37,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Autowired
private HsOfficeDebitorRepository debitorRepo;
+ @Autowired
+ private HsOfficeRelationRepository relRepo;
+
@PersistenceContext
private EntityManager em;
@@ -53,22 +63,44 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override
@Transactional
public ResponseEntity addDebitor(
- final String currentUser,
- final String assumedRoles,
- final HsOfficeDebitorInsertResource body) {
+ String currentUser,
+ String assumedRoles,
+ HsOfficeDebitorInsertResource body) {
context.define(currentUser, assumedRoles);
- final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
+ Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRelUuid() == null,
+ "ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
+ Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
+ "ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
+ Validate.isTrue(body.getDebitorRel() == null ||
+ body.getDebitorRel().getType() == null || DEBITOR.name().equals(body.getDebitorRel().getType()),
+ "ERROR: [400] debitorRel.type must be '"+DEBITOR.name()+"' or null for default");
+ Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
+ "ERROR: [400] debitorRel.mark must be null");
- final var saved = debitorRepo.save(entityToSave);
+ final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
+ if ( body.getDebitorRel() != null ) {
+ body.getDebitorRel().setType(DEBITOR.name());
+ final var debitorRel = mapper.map(body.getDebitorRel(), HsOfficeRelationEntity.class);
+ entityToSave.setDebitorRel(relRepo.save(debitorRel));
+ } else {
+ final var debitorRelOptional = relRepo.findByUuid(body.getDebitorRelUuid());
+ debitorRelOptional.ifPresentOrElse(
+ debitorRel -> {entityToSave.setDebitorRel(relRepo.save(debitorRel));},
+ () -> { throw new EntityNotFoundException("ERROR: [400] debitorRelUuid not found: " + body.getDebitorRelUuid());});
+ }
+
+ final var savedEntity = debitorRepo.save(entityToSave);
+ em.flush();
+ em.refresh(savedEntity);
final var uri =
MvcUriComponentsBuilder.fromController(getClass())
.path("/api/hs/office/debitors/{id}")
- .buildAndExpand(saved.getUuid())
+ .buildAndExpand(savedEntity.getUuid())
.toUri();
- final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
+ final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class);
return ResponseEntity.created(uri).body(mapped);
}
@@ -119,6 +151,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
new HsOfficeDebitorEntityPatcher(em, current).apply(body);
final var saved = debitorRepo.save(current);
+ Hibernate.initialize(saved);
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
return ResponseEntity.ok(mapped);
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 4fb08538..ee8e88a7 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -3,7 +3,6 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
-import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
@@ -12,17 +11,26 @@ import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.annotations.JoinFormula;
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
import java.io.IOException;
-import java.util.Optional;
import java.util.UUID;
+import static jakarta.persistence.CascadeType.DETACH;
+import static jakarta.persistence.CascadeType.MERGE;
+import static jakarta.persistence.CascadeType.PERSIST;
+import static jakarta.persistence.CascadeType.REFRESH;
+import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -30,7 +38,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Table(name = "hs_office_debitor_rv")
@Getter
@Setter
-@Builder
+@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Debitor")
@@ -38,15 +46,11 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
- // TODO: I would rather like to generate something matching this example:
- // debitor(1234500: Test AG, tes)
- // maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)?
private static Stringify stringify =
stringify(HsOfficeDebitorEntity.class, "debitor")
- .withProp(e -> DEBITOR_NUMBER_TAG + e.getDebitorNumber())
- .withProp(HsOfficeDebitorEntity::getPartner)
+ .withIdProp(HsOfficeDebitorEntity::toShortString)
+ .withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationEntity::toShortString).orElse(null))
.withProp(HsOfficeDebitorEntity::getDefaultPrefix)
- .withSeparator(": ")
.quotedValues(false);
@Id
@@ -55,15 +59,28 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private UUID uuid;
@ManyToOne
- @JoinColumn(name = "partneruuid")
+ @JoinFormula(
+ referencedColumnName = "uuid",
+ value = """
+ (
+ SELECT DISTINCT partner.uuid
+ FROM hs_office_partner_rv partner
+ JOIN hs_office_relation_rv dRel
+ ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR'
+ JOIN hs_office_relation_rv pRel
+ ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER'
+ WHERE pRel.holderUuid = dRel.anchorUuid
+ )
+ """)
+ @NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerEntity partner;
@Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
- @ManyToOne
- @JoinColumn(name = "billingcontactuuid")
- private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson
+ @ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
+ @JoinColumn(name = "debitorreluuid", nullable = false)
+ private HsOfficeRelationEntity debitorRel;
@Column(name = "billable", nullable = false)
private Boolean billable; // not a primitive because otherwise the default would be false
@@ -88,14 +105,16 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private String defaultPrefix;
private String getDebitorNumberString() {
- if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null) {
- return null;
- }
- return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix);
+ return ofNullable(partner)
+ .filter(partner -> debitorNumberSuffix != null)
+ .map(HsOfficePartnerEntity::getPartnerNumber)
+ .map(Object::toString)
+ .map(partnerNumber -> partnerNumber + String.format("%02d", debitorNumberSuffix))
+ .orElse(null);
}
public Integer getDebitorNumber() {
- return Optional.ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
+ return ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
}
@Override
@@ -111,28 +130,28 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public static RbacView rbac() {
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
.withIdentityView(SQL.query("""
- SELECT debitor.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 partnerRel.type = 'DEBITOR'
- WHERE debitorRel.uuid = debitor.debitorRelUuid)
- || to_char(debitorNumberSuffix, 'fm00')
- from hs_office_debitor as debitor
+ 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
"""))
+ .withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
.withUpdatableColumns(
- "debitorRel",
+ "debitorRelUuid",
"billable",
- "debitorUuid",
"refundBankAccountUuid",
"vatId",
"vatCountryCode",
"vatBusiness",
"vatReverseCharge",
"defaultPrefix" /* TODO: do we want that updatable? */)
- .createPermission(INSERT).grantedTo("global", ADMIN)
+ .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
directlyFetchedByDependsOnColumn(),
@@ -149,9 +168,16 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
- dependsOnColumn("partnerRelUuid"),
- directlyFetchedByDependsOnColumn(),
- NULLABLE)
+ dependsOnColumn("debitorRelUuid"),
+ fetchedBySql("""
+ SELECT ${columns}
+ 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 ${REF}.debitorRelUuid = debitorRel.uuid
+ """),
+ NOT_NULL)
.toRole("partnerRel", ADMIN).grantRole("debitorRel", ADMIN)
.toRole("partnerRel", AGENT).grantRole("debitorRel", AGENT)
.toRole("debitorRel", AGENT).grantRole("partnerRel", TENANT)
@@ -162,6 +188,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated");
+ rbac().generateWithBaseFileName("273-hs-office-debitor-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java
index 914c8230..cd50abf8 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java
@@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
-import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@@ -23,9 +23,9 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher {
- verifyNotNull(newValue, "billingContact");
- entity.setBillingContact(em.getReference(HsOfficeContactEntity.class, newValue));
+ OptionalFromJson.of(resource.getDebitorRelUuid()).ifPresent(newValue -> {
+ verifyNotNull(newValue, "debitorRel");
+ entity.setDebitorRel(em.getReference(HsOfficeRelationEntity.class, newValue));
});
Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable);
OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java
index 64be98b1..737c24ba 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java
@@ -13,7 +13,10 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByDebitorNumber(int partnerNumber, byte debitorNumberSuffix);
@@ -24,9 +27,15 @@ public interface HsOfficeDebitorRepository extends Repository> listMemberships(
@@ -121,7 +116,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
final var current = membershipRepo.findByUuid(membershipUuid).orElseThrow();
- new HsOfficeMembershipEntityPatcher(em, mapper, current).apply(body);
+ new HsOfficeMembershipEntityPatcher(mapper, current).apply(body);
final var saved = membershipRepo.save(current);
final var mapped = mapper.map(saved, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index 9861f727..c4a4c8b9 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -4,20 +4,30 @@ import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
import com.vladmihalcea.hibernate.type.range.Range;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
-import org.hibernate.annotations.Fetch;
-import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.Type;
import jakarta.persistence.*;
+import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -35,10 +45,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
private static Stringify stringify = stringify(HsOfficeMembershipEntity.class)
.withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
.withProp(e -> e.getPartner().toShortString())
- .withProp(e -> e.getMainDebitor().toShortString())
.withProp(e -> e.getValidity().asString())
.withProp(HsOfficeMembershipEntity::getReasonForTermination)
- .withSeparator(", ")
.quotedValues(false);
@Id
@@ -49,11 +57,6 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
@JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner;
- @ManyToOne
- @Fetch(FetchMode.JOIN)
- @JoinColumn(name = "maindebitoruuid")
- private HsOfficeDebitorEntity mainDebitor;
-
@Column(name = "membernumbersuffix", length = 2)
private String memberNumberSuffix;
@@ -114,4 +117,45 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
setReasonForTermination(HsOfficeReasonForTermination.NONE);
}
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("membership", HsOfficeMembershipEntity.class)
+ .withIdentityView(SQL.query("""
+ 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
+ """))
+ .withRestrictedViewOrderBy(SQL.projection("validity"))
+ .withUpdatableColumns("validity", "membershipFeeBillable", "reasonForTermination")
+
+ .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
+ dependsOnColumn("partnerUuid"),
+ fetchedBySql("""
+ SELECT ${columns}
+ FROM hs_office_partner AS partner
+ JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
+ WHERE partner.uuid = ${REF}.partnerUuid
+ """),
+ NOT_NULL)
+ .toRole("global", ADMIN).grantPermission(INSERT)
+
+ .createRole(OWNER, (with) -> {
+ with.owningUser(CREATOR);
+ with.incomingSuperRole("partnerRel", ADMIN);
+ with.permission(DELETE);
+ })
+ .createSubRole(ADMIN, (with) -> {
+ with.incomingSuperRole("partnerRel", AGENT);
+ with.permission(UPDATE);
+ })
+ .createSubRole(REFERRER, (with) -> {
+ with.outgoingSubRole("partnerRel", TENANT);
+ with.permission(SELECT);
+ });
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("303-hs-office-membership-rbac");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java
index 59fa6070..89933fe8 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java
@@ -1,37 +1,26 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
-import jakarta.persistence.EntityManager;
import java.util.Optional;
-import java.util.UUID;
public class HsOfficeMembershipEntityPatcher implements EntityPatcher {
- private final EntityManager em;
private final Mapper mapper;
private final HsOfficeMembershipEntity entity;
public HsOfficeMembershipEntityPatcher(
- final EntityManager em,
final Mapper mapper,
final HsOfficeMembershipEntity entity) {
- this.em = em;
this.mapper = mapper;
this.entity = entity;
}
@Override
public void apply(final HsOfficeMembershipPatchResource resource) {
- OptionalFromJson.of(resource.getMainDebitorUuid())
- .ifPresent(newValue -> {
- verifyNotNull(newValue, "debitor");
- entity.setMainDebitor(em.getReference(HsOfficeDebitorEntity.class, newValue));
- });
OptionalFromJson.of(resource.getValidTo()).ifPresent(
entity::setValidTo);
Optional.ofNullable(resource.getReasonForTermination())
@@ -40,10 +29,4 @@ public class HsOfficeMembershipEntityPatcher implements EntityPatcher stringify = stringify(HsOfficePartnerEntity.class, "partner")
- .withProp(HsOfficePartnerEntity::getPerson)
- .withProp(HsOfficePartnerEntity::getContact)
- .withSeparator(": ")
+ .withIdProp(HsOfficePartnerEntity::toShortString)
+ .withProp(p -> ofNullable(p.getPartnerRel())
+ .map(HsOfficeRelationEntity::getHolder)
+ .map(HsOfficePersonEntity::toShortString)
+ .orElse(null))
+ .withProp(p -> ofNullable(p.getPartnerRel())
+ .map(HsOfficeRelationEntity::getContact)
+ .map(HsOfficeContactEntity::toShortString)
+ .orElse(null))
.quotedValues(false);
@Id
@@ -49,25 +68,19 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber;
- @ManyToOne
+ @ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
@JoinColumn(name = "partnerreluuid", nullable = false)
private HsOfficeRelationEntity partnerRel;
- // TODO: remove, is replaced by partnerRel
- @ManyToOne
- @JoinColumn(name = "personuuid", nullable = false)
- private HsOfficePersonEntity person;
-
- // TODO: remove, is replaced by partnerRel
- @ManyToOne
- @JoinColumn(name = "contactuuid", nullable = false)
- private HsOfficeContactEntity contact;
-
- @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true)
+ @ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true)
@JoinColumn(name = "detailsuuid")
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details;
+ public String getTaggedPartnerNumber() {
+ return PARTNER_NUMBER_TAG + partnerNumber;
+ }
+
@Override
public String toString() {
return stringify.apply(this);
@@ -75,22 +88,14 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@Override
public String toShortString() {
- return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("");
+ return getTaggedPartnerNumber();
}
public static RbacView rbac() {
return rbacViewFor("partner", HsOfficePartnerEntity.class)
- .withIdentityView(SQL.query("""
- SELECT partner.partnerNumber
- || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
- || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
- FROM hs_office_partner AS partner
- """))
- .withUpdatableColumns(
- "partnerRelUuid",
- "personUuid",
- "contactUuid")
- .createPermission(INSERT).grantedTo("global", ADMIN)
+ .withIdentityView(SQL.projection("'P-' || partnerNumber"))
+ .withUpdatableColumns("partnerRelUuid")
+ .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationEntity.class,
directlyFetchedByDependsOnColumn(),
@@ -108,6 +113,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("233-hs-office-partner-rbac-generated");
+ rbac().generateWithBaseFileName("233-hs-office-partner-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcher.java
index bc5de4d7..e43009c5 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcher.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcher.java
@@ -1,13 +1,11 @@
package net.hostsharing.hsadminng.hs.office.partner;
-import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
-import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
import jakarta.persistence.EntityManager;
-import java.util.UUID;
class HsOfficePartnerEntityPatcher implements EntityPatcher {
private final EntityManager em;
@@ -21,19 +19,15 @@ class HsOfficePartnerEntityPatcher implements EntityPatcher {
- verifyNotNull(newValue, "contact");
- entity.setContact(em.getReference(HsOfficeContactEntity.class, newValue));
- });
- OptionalFromJson.of(resource.getPersonUuid()).ifPresent(newValue -> {
- verifyNotNull(newValue, "person");
- entity.setPerson(em.getReference(HsOfficePersonEntity.class, newValue));
+ OptionalFromJson.of(resource.getPartnerRelUuid()).ifPresent(newValue -> {
+ verifyNotNull(newValue, "partnerRel");
+ entity.setPartnerRel(em.getReference(HsOfficeRelationEntity.class, newValue));
});
new HsOfficePartnerDetailsEntityPatcher(em, entity.getDetails()).apply(resource.getDetails());
}
- private void verifyNotNull(final UUID newValue, final String propertyName) {
+ private void verifyNotNull(final Object newValue, final String propertyName) {
if (newValue == null) {
throw new IllegalArgumentException("property '" + propertyName + "' must not be null");
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
index dfbd1667..d334c741 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
@@ -11,10 +11,13 @@ public interface HsOfficePartnerRepository extends Repository findByUuid(UUID id);
+ List findAll(); // TODO: move to a repo in test sources
+
@Query("""
SELECT partner FROM HsOfficePartnerEntity partner
- JOIN HsOfficeContactEntity contact ON contact.uuid = partner.contact.uuid
- JOIN HsOfficePersonEntity person ON person.uuid = partner.person.uuid
+ JOIN HsOfficeRelationEntity rel ON rel.uuid = partner.partnerRel.uuid
+ JOIN HsOfficeContactEntity contact ON contact.uuid = rel.contact.uuid
+ JOIN HsOfficePersonEntity person ON person.uuid = rel.holder.uuid
WHERE :name is null
OR partner.details.birthName like concat(cast(:name as text), '%')
OR contact.label like concat(cast(:name as text), '%')
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
index fcc89dde..b930f9b6 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
@@ -69,6 +69,8 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
return rbacViewFor("person", HsOfficePersonEntity.class)
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
+ .toRole("global", GUEST).grantPermission(INSERT)
+
.createRole(OWNER, (with) -> {
with.permission(DELETE);
with.owningUser(CREATOR);
@@ -84,6 +86,6 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
+ rbac().generateWithBaseFileName("213-hs-office-person-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
index 364368af..5301983f 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
@@ -16,7 +16,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@@ -92,22 +92,26 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class,
dependsOnColumn("anchorUuid"),
directlyFetchedByDependsOnColumn(),
- NULLABLE)
+ NOT_NULL)
.importEntityAlias("holderPerson", HsOfficePersonEntity.class,
dependsOnColumn("holderUuid"),
directlyFetchedByDependsOnColumn(),
- NULLABLE)
+ NOT_NULL)
.importEntityAlias("contact", HsOfficeContactEntity.class,
dependsOnColumn("contactUuid"),
directlyFetchedByDependsOnColumn(),
- NULLABLE)
+ NOT_NULL)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
with.incomingSuperRole(GLOBAL, ADMIN);
+ // TODO: if type=REPRESENTATIIVE
+ // with.incomingSuperRole("holderPerson", ADMIN);
with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
with.incomingSuperRole("anchorPerson", ADMIN);
+ // TODO: if type=REPRESENTATIIVE
+ // with.outgoingSuperRole("anchorPerson", OWNER);
with.permission(UPDATE);
})
.createSubRole(AGENT, (with) -> {
@@ -120,10 +124,12 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
with.outgoingSubRole("holderPerson", REFERRER);
with.outgoingSubRole("contact", REFERRER);
with.permission(SELECT);
- });
+ })
+
+ .toRole("anchorPerson", ADMIN).grantPermission(INSERT);
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("223-hs-office-relation-rbac-generated");
+ rbac().generateWithBaseFileName("223-hs-office-relation-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
index 581cd577..364f4ba4 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
@@ -132,6 +132,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
if (entity.getValidity().hasUpperBound()) {
resource.setValidTo(entity.getValidity().upper().minusDays(1));
}
+ resource.getDebitor().setDebitorNumber(entity.getDebitor().getDebitorNumber());
};
final BiConsumer SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index 1b8135ba..897f89b8 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -21,6 +21,7 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
@@ -43,7 +44,6 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
.withProp(HsOfficeSepaMandateEntity::getReference)
.withProp(HsOfficeSepaMandateEntity::getAgreement)
.withProp(e -> e.getValidity().asString())
- .withSeparator(", ")
.quotedValues(false);
@Id
@@ -96,11 +96,27 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
public static RbacView rbac() {
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
- .withIdentityView(projection("concat(tradeName, familyName, givenName)"))
+ .withIdentityView(query("""
+ 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
+ """))
+ .withRestrictedViewOrderBy(expression("validity"))
.withUpdatableColumns("reference", "agreement", "validity")
- .importEntityAlias("debitorRel", HsOfficeRelationEntity.class, dependsOnColumn("debitorRelUuid"))
- .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class, dependsOnColumn("bankAccountUuid"))
+ .importEntityAlias("debitorRel", HsOfficeRelationEntity.class,
+ dependsOnColumn("debitorUuid"),
+ fetchedBySql("""
+ SELECT ${columns}
+ FROM hs_office_relation debitorRel
+ JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
+ WHERE debitor.uuid = ${REF}.debitorUuid
+ """),
+ NOT_NULL)
+ .importEntityAlias("bankAccount", HsOfficeBankAccountEntity.class,
+ dependsOnColumn("bankAccountUuid"),
+ directlyFetchedByDependsOnColumn(),
+ NOT_NULL)
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
@@ -119,10 +135,12 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
with.incomingSuperRole("debitorRel", AGENT);
with.outgoingSubRole("debitorRel", TENANT);
with.permission(SELECT);
- });
+ })
+
+ .toRole("debitorRel", ADMIN).grantPermission(INSERT);
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
+ rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
index 2e0a4a2f..a9a72160 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
@@ -47,16 +47,14 @@ public class InsertTriggerGenerator {
do language plpgsql $$
declare
row ${rawSuperTableName};
- permissionUuid uuid;
- roleUuid uuid;
begin
call defineContext('create INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows');
FOR row IN SELECT * FROM ${rawSuperTableName}
LOOP
- roleUuid := findRoleId(${rawSuperRoleDescriptor});
- permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
- call grantPermissionToRole(permissionUuid, roleUuid);
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', '${rawSubTableName}'),
+ ${rawSuperRoleDescriptor});
END LOOP;
END;
$$;
diff --git a/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java b/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java
index 076f6209..8cdf433b 100644
--- a/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java
+++ b/src/main/java/net/hostsharing/hsadminng/stringify/Stringify.java
@@ -16,6 +16,7 @@ public final class Stringify {
private final Class clazz;
private final String name;
+ private Function idProp;
private final List> props = new ArrayList<>();
private String separator = ", ";
private Boolean quotedValues = null;
@@ -42,6 +43,11 @@ public final class Stringify {
}
}
+ public Stringify withIdProp(final Function getter) {
+ idProp = getter;
+ return this;
+ }
+
public Stringify withProp(final String propName, final Function getter) {
props.add(new Property<>(propName, getter));
return this;
@@ -64,7 +70,9 @@ public final class Stringify {
})
.map(propVal -> propName(propVal, "=") + optionallyQuoted(propVal))
.collect(Collectors.joining(separator));
- return name + "(" + propValues + ")";
+ return idProp != null
+ ? name + "(" + idProp.apply(object) + ": " + propValues + ")"
+ : name + "(" + propValues + ")";
}
public Stringify withSeparator(final String separator) {
diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml
index 26736fac..dcf3df93 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml
@@ -9,6 +9,8 @@ components:
uuid:
type: string
format: uuid
+ debitorRel:
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
debitorNumber:
type: integer
format: int32
@@ -21,8 +23,6 @@ components:
maximum: 99
partner:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
- billingContact:
- $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
billable:
type: boolean
vatId:
@@ -43,7 +43,7 @@ components:
HsOfficeDebitorPatch:
type: object
properties:
- billingContactUuid:
+ debitorRelUuid:
type: string
format: uuid
nullable: true
@@ -75,14 +75,11 @@ components:
HsOfficeDebitorInsert:
type: object
properties:
- partnerUuid:
+ debitorRel:
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
+ debitorRelUuid:
type: string
format: uuid
- nullable: false
- billingContactUuid:
- type: string
- format: uuid
- nullable: false
debitorNumberSuffix:
type: integer
format: int8
@@ -105,9 +102,7 @@ components:
defaultPrefix:
type: string
pattern: '^[a-z]{3}$'
-
required:
- - partnerUuid
- - billingContactUuid
+ - debitorNumberSuffix
- defaultPrefix
- billable
diff --git a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml
index 163f6f34..02fba043 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml
@@ -46,10 +46,6 @@ components:
HsOfficeMembershipPatch:
type: object
properties:
- mainDebitorUuid:
- type: string
- format: uuid
- nullable: true
validTo:
type: string
format: date
@@ -69,10 +65,6 @@ components:
type: string
format: uuid
nullable: false
- mainDebitorUuid:
- type: string
- format: uuid
- nullable: false
memberNumberSuffix:
type: string
minLength: 2
@@ -95,7 +87,6 @@ components:
required:
- partnerUuid
- memberNumberSuffix
- - mainDebitorUuid
- validFrom
- membershipFeeBillable
additionalProperties: false
diff --git a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
index eb544c8d..89b22241 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml
@@ -14,10 +14,8 @@ components:
format: int8
minimum: 10000
maximum: 99999
- person:
- $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
- contact:
- $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
+ partnerRel:
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
details:
$ref: '#/components/schemas/HsOfficePartnerDetails'
@@ -52,11 +50,7 @@ components:
HsOfficePartnerPatch:
type: object
properties:
- personUuid:
- type: string
- format: uuid
- nullable: true
- contactUuid:
+ partnerRelUuid:
type: string
format: uuid
nullable: true
@@ -98,18 +92,11 @@ components:
maximum: 99999
partnerRel:
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
- personUuid:
- type: string
- format: uuid
- contactUuid:
- type: string
- format: uuid
details:
$ref: '#/components/schemas/HsOfficePartnerDetailsInsert'
required:
- partnerNumber
- - personUuid
- - contactUuid
+ - partnerRel
- details
HsOfficePartnerRelInsert:
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml
similarity index 100%
rename from src/main/resources/api-definition/hs-office/hs-office-relations-schemas.yaml
rename to src/main/resources/api-definition/hs-office/hs-office-relation-schemas.yaml
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
index 4511b895..83b9cf3e 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations-with-uuid.yaml
@@ -19,7 +19,7 @@ get:
content:
'application/json':
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
@@ -44,14 +44,14 @@ patch:
content:
'application/json':
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
responses:
"200":
description: OK
content:
'application/json':
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
diff --git a/src/main/resources/api-definition/hs-office/hs-office-relations.yaml b/src/main/resources/api-definition/hs-office/hs-office-relations.yaml
index 6328974f..0c98075f 100644
--- a/src/main/resources/api-definition/hs-office/hs-office-relations.yaml
+++ b/src/main/resources/api-definition/hs-office/hs-office-relations.yaml
@@ -18,7 +18,7 @@ get:
in: query
required: false
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationType'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType'
description: Prefix of name properties from holder or contact to filter the results.
responses:
"200":
@@ -28,7 +28,7 @@ get:
schema:
type: array
items:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
@@ -46,7 +46,7 @@ post:
content:
'application/json':
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
required: true
responses:
"201":
@@ -54,7 +54,7 @@ post:
content:
'application/json':
schema:
- $ref: './hs-office-relations-schemas.yaml#/components/schemas/HsOfficeRelation'
+ $ref: './hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
"401":
$ref: './error-responses.yaml#/components/responses/Unauthorized'
"403":
diff --git a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
index 589c00b8..ff0e18e4 100644
--- a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
+++ b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
@@ -22,5 +22,6 @@ components:
- owner
- admin
- tenant
+ - referrer
roleName:
type: string
diff --git a/src/main/resources/db/changelog/006-numeric-hash-functions.sql b/src/main/resources/db/changelog/006-numeric-hash-functions.sql
index 5e2e2814..13d31931 100644
--- a/src/main/resources/db/changelog/006-numeric-hash-functions.sql
+++ b/src/main/resources/db/changelog/006-numeric-hash-functions.sql
@@ -3,7 +3,7 @@
-- ============================================================================
-- NUMERIC-HASH-FUNCTIONS
---changeset hash:1 endDelimiter:--//
+--changeset numeric-hash-functions:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create function bigIntHash(text) returns bigint as $$
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql
index 0e5cc457..66ebacc3 100644
--- a/src/main/resources/db/changelog/010-context.sql
+++ b/src/main/resources/db/changelog/010-context.sql
@@ -26,7 +26,7 @@ create or replace procedure defineContext(
currentTask varchar(96),
currentRequest text = null,
currentUser varchar(63) = null,
- assumedRoles varchar(256) = null
+ assumedRoles varchar(1023) = null
)
language plpgsql as $$
begin
@@ -43,7 +43,7 @@ begin
execute format('set local hsadminng.currentUser to %L', currentUser);
assumedRoles := coalesce(assumedRoles, '');
- assert length(assumedRoles) <= 256, FORMAT('assumedRoles must not be longer than 256 characters: "%s"', assumedRoles);
+ assert length(assumedRoles) <= 1023, FORMAT('assumedRoles must not be longer than 1023 characters: "%s"', assumedRoles);
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
@@ -87,11 +87,11 @@ end; $$;
Raises exception if not set.
*/
create or replace function currentRequest()
- returns varchar(512)
+ returns text
stable -- leakproof
language plpgsql as $$
declare
- currentRequest varchar(512);
+ currentRequest text;
begin
begin
currentRequest := current_setting('hsadminng.currentRequest');
@@ -135,22 +135,11 @@ end; $$;
or empty array, if not set.
*/
create or replace function assumedRoles()
- returns varchar(63)[]
+ returns varchar(1023)[]
stable -- leakproof
language plpgsql as $$
-declare
- currentSubject varchar(63);
begin
- begin
- currentSubject := current_setting('hsadminng.assumedRoles');
- exception
- when others then
- return array []::varchar[];
- end;
- if (currentSubject = '') then
- return array []::varchar[];
- end if;
- return string_to_array(currentSubject, ';');
+ return string_to_array(current_setting('hsadminng.assumedRoles', true), ';');
end; $$;
create or replace function cleanIdentifier(rawIdentifier varchar)
@@ -219,17 +208,17 @@ begin
end ; $$;
create or replace function currentSubjects()
- returns varchar(63)[]
+ returns varchar(1023)[]
stable -- leakproof
language plpgsql as $$
declare
- assumedRoles varchar(63)[];
+ assumedRoles varchar(1023)[];
begin
assumedRoles := assumedRoles();
if array_length(assumedRoles, 1) > 0 then
- return assumedRoles();
+ return assumedRoles;
else
- return array [currentUser()]::varchar(63)[];
+ return array [currentUser()]::varchar(1023)[];
end if;
end; $$;
diff --git a/src/main/resources/db/changelog/020-audit-log.sql b/src/main/resources/db/changelog/020-audit-log.sql
index ec14ad0d..2491218d 100644
--- a/src/main/resources/db/changelog/020-audit-log.sql
+++ b/src/main/resources/db/changelog/020-audit-log.sql
@@ -27,9 +27,9 @@ create table tx_context
txId bigint not null,
txTimestamp timestamp not null,
currentUser varchar(63) not null, -- not the uuid, because users can be deleted
- assumedRoles varchar(256) not null, -- not the uuids, because roles can be deleted
+ assumedRoles varchar(1023) not null, -- not the uuids, because roles can be deleted
currentTask varchar(96) not null,
- currentRequest text not null
+ currentRequest text not null
);
create index on tx_context using brin (txTimestamp);
diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql
index b494d120..408c3594 100644
--- a/src/main/resources/db/changelog/055-rbac-views.sql
+++ b/src/main/resources/db/changelog/055-rbac-views.sql
@@ -63,6 +63,7 @@ create or replace view rbacgrants_ev as
x.grantedByRoleUuid,
x.ascendantUuid as ascendantUuid,
x.descendantUuid as descendantUuid,
+ x.op as permOp, x.optablename as permOpTableName,
x.assumed
from (
select g.uuid as grantUuid,
@@ -74,13 +75,17 @@ create or replace view rbacgrants_ev as
'role ' || aro.objectTable || '#' || findIdNameByObjectUuid(aro.objectTable, aro.uuid) || '.' || ar.roletype
) as ascendingIdName,
aro.objectTable, aro.uuid,
-
- coalesce(
- 'role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype,
- 'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
+ ( case
+ when dro is not null
+ then ('role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype)
+ when dp.op = 'INSERT'
+ then 'perm ' || dp.op || ' into ' || dp.opTableName || ' with ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
+ else 'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
+ end
) as descendingIdName,
- dro.objectTable, dro.uuid
- from rbacgrants as g
+ dro.objectTable, dro.uuid,
+ dp.op, dp.optablename
+ from rbacgrants as g
left outer join rbacrole as ar on ar.uuid = g.ascendantUuid
left outer join rbacobject as aro on aro.uuid = ar.objectuuid
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/113-test-customer-rbac.sql
index 874cbc9a..fd460049 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.sql
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql
@@ -86,16 +86,14 @@ execute procedure insertTriggerForTestCustomer_tf();
do language plpgsql $$
declare
row global;
- permissionUuid uuid;
- roleUuid uuid;
begin
call defineContext('create INSERT INTO test_customer permissions for the related global rows');
FOR row IN SELECT * FROM global
LOOP
- roleUuid := findRoleId(globalAdmin());
- permissionUuid := createPermission(row.uuid, 'INSERT', 'test_customer');
- call grantPermissionToRole(permissionUuid, roleUuid);
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', 'test_customer'),
+ globalAdmin());
END LOOP;
END;
$$;
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql
index 070d3fcc..972b174d 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.sql
+++ b/src/main/resources/db/changelog/123-test-package-rbac.sql
@@ -151,16 +151,14 @@ execute procedure updateTriggerForTestPackage_tf();
do language plpgsql $$
declare
row test_customer;
- permissionUuid uuid;
- roleUuid uuid;
begin
call defineContext('create INSERT INTO test_package permissions for the related test_customer rows');
FOR row IN SELECT * FROM test_customer
LOOP
- roleUuid := findRoleId(testCustomerAdmin(row));
- permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
- call grantPermissionToRole(permissionUuid, roleUuid);
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', 'test_package'),
+ testCustomerAdmin(row));
END LOOP;
END;
$$;
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql
index bef72697..7a891841 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.sql
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql
@@ -150,16 +150,14 @@ execute procedure updateTriggerForTestDomain_tf();
do language plpgsql $$
declare
row test_package;
- permissionUuid uuid;
- roleUuid uuid;
begin
call defineContext('create INSERT INTO test_domain permissions for the related test_package rows');
FOR row IN SELECT * FROM test_package
LOOP
- roleUuid := findRoleId(testPackageAdmin(row));
- permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
- call grantPermissionToRole(permissionUuid, roleUuid);
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', 'test_domain'),
+ testPackageAdmin(row));
END LOOP;
END;
$$;
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
deleted file mode 100644
index 136dad87..00000000
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.sql
+++ /dev/null
@@ -1,126 +0,0 @@
---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:--//
--- ----------------------------------------------------------------------------
-
-/**
- Checks if the user or assumed roles are allowed to insert a row to hs_office_contact,
- where only global-admin has that permission.
-*/
-create or replace function hs_office_contact_insert_permission_missing_tf()
- returns trigger
- language plpgsql as $$
-begin
- raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)',
- currentSubjects(), currentSubjectsUuids();
-end; $$;
-
-create trigger hs_office_contact_insert_permission_check_tg
- before insert on hs_office_contact
- for each row
- when ( not isGlobalAdmin() )
- execute procedure hs_office_contact_insert_permission_missing_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$);
---//
-
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md
similarity index 92%
rename from src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md
rename to src/main/resources/db/changelog/203-hs-office-contact-rbac.md
index f3547312..52584907 100644
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac-generated.md
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md
@@ -24,6 +24,7 @@ subgraph contact["`**contact**`"]
perm:contact:DELETE{{contact:DELETE}}
perm:contact:UPDATE{{contact:UPDATE}}
perm:contact:SELECT{{contact:SELECT}}
+ perm:contact:INSERT{{contact:INSERT}}
end
end
@@ -39,5 +40,6 @@ role:contact:admin ==> role:contact:referrer
role:contact:owner ==> perm:contact:DELETE
role:contact:admin ==> perm:contact:UPDATE
role:contact:referrer ==> perm:contact:SELECT
+role:global:guest ==> perm:contact:INSERT
```
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
index 3a9b0c34..0e08e15f 100644
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--//
@@ -15,127 +17,130 @@ call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact');
-- ============================================================================
---changeset hs-office-contact-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates the roles and their assignments for a new contact for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRolesForHsOfficeContact()
+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
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
-
- perform createRoleWithGrants(
- hsOfficeContactOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
- userUuids => array[currentUserUuid()],
- grantedByRole => globalAdmin()
- );
-
- perform createRoleWithGrants(
- hsOfficeContactAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeContactTenant(NEW),
- incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeContactGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
- );
-
+ call buildRbacSystemForHsOfficeContact(NEW);
return NEW;
end; $$;
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-
-create trigger createRbacRolesForHsOfficeContact_Trigger
- after insert
- on hs_office_contact
+create trigger insertTriggerForHsOfficeContact_tg
+ after insert on hs_office_contact
for each row
-execute procedure createRbacRolesForHsOfficeContact();
+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$
- target.label
+call generateRbacIdentityViewFromProjection('hs_office_contact',
+ $idName$
+ label
$idName$);
--//
-
-- ============================================================================
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_contact', 'target.label',
+call generateRbacRestrictedView('hs_office_contact',
+ $orderBy$
+ label
+ $orderBy$,
$updates$
label = new.label,
postalAddress = new.postalAddress,
emailAddresses = new.emailAddresses,
phoneNumbers = new.phoneNumbers
$updates$);
---/
-
-
--- ============================================================================
---changeset hs-office-contact-rbac-NEW-CONTACT:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-contact and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid;
- begin
- call defineContext('granting global new-contact permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-contact']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeContactNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-contact 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_contact_insert_trigger
- before insert
- on hs_office_contact
- for each row
- -- TODO.spec: who is allowed to create new contacts
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeContactNotAllowedForCurrentSubjects();
--//
diff --git a/src/main/resources/db/changelog/210-hs-office-person.sql b/src/main/resources/db/changelog/210-hs-office-person.sql
index 6a331277..dd91857f 100644
--- a/src/main/resources/db/changelog/210-hs-office-person.sql
+++ b/src/main/resources/db/changelog/210-hs-office-person.sql
@@ -22,7 +22,6 @@ create table if not exists hs_office_person
givenName varchar(48),
familyName varchar(48)
);
---//
-- ============================================================================
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
deleted file mode 100644
index f99c2a46..00000000
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.sql
+++ /dev/null
@@ -1,126 +0,0 @@
---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:--//
--- ----------------------------------------------------------------------------
-
-/**
- Checks if the user or assumed roles are allowed to insert a row to hs_office_person,
- where only global-admin has that permission.
-*/
-create or replace function hs_office_person_insert_permission_missing_tf()
- returns trigger
- language plpgsql as $$
-begin
- raise exception '[403] insert into hs_office_person not allowed for current subjects % (%)',
- currentSubjects(), currentSubjectsUuids();
-end; $$;
-
-create trigger hs_office_person_insert_permission_check_tg
- before insert on hs_office_person
- for each row
- when ( not isGlobalAdmin() )
- execute procedure hs_office_person_insert_permission_missing_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$);
---//
-
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md b/src/main/resources/db/changelog/213-hs-office-person-rbac.md
similarity index 92%
rename from src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md
rename to src/main/resources/db/changelog/213-hs-office-person-rbac.md
index aa971642..70e0f33a 100644
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac-generated.md
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.md
@@ -21,6 +21,7 @@ subgraph person["`**person**`"]
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}}
@@ -36,6 +37,7 @@ 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
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
index fbb1f8e1..adbdae33 100644
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
@@ -15,74 +17,125 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
-- ============================================================================
---changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
+
/*
- Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRolesForHsOfficePerson()
+
+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
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
-
- perform createRoleWithGrants(
- hsOfficePersonOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
- userUuids => array[currentUserUuid()],
- grantedByRole => globalAdmin()
- );
-
- -- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
- perform createRoleWithGrants(
- hsOfficePersonAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficePersonTenant(NEW),
- incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficePersonGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
- );
-
+ call buildRbacSystemForHsOfficePerson(NEW);
return NEW;
end; $$;
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-
-create trigger createRbacRolesForHsOfficePerson_Trigger
- after insert
- on hs_office_person
+create trigger insertTriggerForHsOfficePerson_tg
+ after insert on hs_office_person
for each row
-execute procedure createRbacRolesForHsOfficePerson();
+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(target.tradeName, target.familyName, target.givenName)
+
+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', 'concat(target.tradeName, target.familyName, target.givenName)',
+call generateRbacRestrictedView('hs_office_person',
+ $orderBy$
+ concat(tradeName, familyName, givenName)
+ $orderBy$,
$updates$
personType = new.personType,
tradeName = new.tradeName,
@@ -91,49 +144,3 @@ call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, ta
$updates$);
--//
-
--- ============================================================================
---changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-person and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-person permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-person']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficePersonNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-person 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_person_insert_trigger
- before insert
- on hs_office_person
- for each row
- -- TODO.spec: who is allowed to create new persons
- when ( not hasAssumedRole() )
-execute procedure addHsOfficePersonNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/218-hs-office-person-test-data.sql b/src/main/resources/db/changelog/218-hs-office-person-test-data.sql
index 6d087754..775ecaa6 100644
--- a/src/main/resources/db/changelog/218-hs-office-person-test-data.sql
+++ b/src/main/resources/db/changelog/218-hs-office-person-test-data.sql
@@ -28,7 +28,7 @@ begin
call defineContext(currentTask, null, emailAddr);
execute format('set local hsadminng.currentTask to %L', currentTask);
- raise notice 'creating test person: %', fullName;
+ raise notice 'creating test person: % by %', fullName, emailAddr;
insert
into hs_office_person (persontype, tradename, givenname, familyname)
values (newPersonType, newTradeName, newGivenName, newFamilyName);
@@ -67,9 +67,10 @@ do language plpgsql $$
call createHsOfficePersonTestData('NP', null, 'Fouler', 'Ellie');
call createHsOfficePersonTestData('LP', 'Second e.K.', 'Smith', 'Peter');
call createHsOfficePersonTestData('IF', 'Third OHG');
- call createHsOfficePersonTestData('IF', 'Fourth eG');
+ 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;
$$;
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
deleted file mode 100644
index 14f797eb..00000000
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.md
+++ /dev/null
@@ -1,100 +0,0 @@
-### 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}}
- 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
-
-```
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
deleted file mode 100644
index 5301dc56..00000000
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac-generated.sql
+++ /dev/null
@@ -1,191 +0,0 @@
---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;
-
- SELECT * FROM hs_office_person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
-
- SELECT * FROM hs_office_contact WHERE uuid = NEW.contactUuid INTO newContact;
-
- 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 $$
-begin
-
- if NEW.contactUuid is distinct from OLD.contactUuid then
- delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
- call buildRbacSystemForHsOfficeRelation(NEW);
- end if;
-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:--//
--- ----------------------------------------------------------------------------
-
-/**
- Checks if the user or assumed roles are allowed to insert a row to hs_office_relation,
- where only global-admin has that permission.
-*/
-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 isGlobalAdmin() )
- 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$);
---//
-
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
index 40691f38..8e5524ec 100644
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
@@ -1,44 +1,102 @@
-### hs_office_relation RBAC
+### rbac relation
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
-
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill:#eee
-
- role:global.admin[global.admin]
-end
-
-subgraph hsOfficeContact
+subgraph holderPerson["`**holderPerson**`"]
direction TB
- style hsOfficeContact fill:#eee
-
- role:hsOfficeContact.admin[contact.admin]
- --> role:hsOfficeContact.tenant[contact.tenant]
- --> role:hsOfficeContact.guest[contact.guest]
+ 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 hsOfficePerson
+subgraph anchorPerson["`**anchorPerson**`"]
direction TB
- style hsOfficePerson fill:#eee
-
- role:hsOfficePerson.admin[person.admin]
- --> role:hsOfficePerson.tenant[person.tenant]
- --> role:hsOfficePerson.guest[person.guest]
+ 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 hsOfficeRelation
+subgraph contact["`**contact**`"]
+ direction TB
+ style contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- role:hsOfficePerson#anchor.admin[person#anchor.admin]
- --- role:hsOfficePerson.admin
-
- role:hsOfficeRelation.owner[relation.owner]
- %% permissions
- role:hsOfficeRelation.owner --> perm:hsOfficeRelation.*{{relation.*}}
- %% incoming
- role:global.admin ---> role:hsOfficeRelation.owner
- role:hsOfficePersonAdmin#anchor.admin
+ 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
+
```
-
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
index 6a7d55a1..6c9ae616 100644
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-relation-rbac-OBJECT:1 endDelimiter:--//
@@ -15,178 +17,255 @@ call generateRbacRoleDescriptors('hsOfficeRelation', 'hs_office_relation');
-- ============================================================================
---changeset hs-office-relation-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for relation entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeRelationRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeRelation(
+ NEW hs_office_relation
+)
+ language plpgsql as $$
+
declare
- hsOfficeRelationTenant RbacRoleDescriptor;
- newAnchor hs_office_person;
- newHolder hs_office_person;
- oldContact hs_office_contact;
- newContact hs_office_contact;
+ newHolderPerson hs_office_person;
+ newAnchorPerson hs_office_person;
+ newContact hs_office_contact;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- hsOfficeRelationTenant := hsOfficeRelationTenant(NEW);
+ 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 as p where p.uuid = NEW.anchorUuid into newAnchor;
- select * from hs_office_person as p where p.uuid = NEW.holderUuid into newHolder;
- select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
+ 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);
- if TG_OP = 'INSERT' then
+ 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(),
- hsOfficePersonAdmin(newAnchor)]
- );
- perform createRoleWithGrants(
- hsOfficeRelationAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeRelationOwner(NEW)]
- );
+ perform createRoleWithGrants(
+ hsOfficeRelationOwner(NEW),
+ permissions => array['DELETE'],
+ incomingSuperRoles => array[globalAdmin()],
+ userUuids => array[currentUserUuid()]
+ );
- -- the tenant role for those related users who can view the data
- perform createRoleWithGrants(
- hsOfficeRelationTenant,
- permissions => array['SELECT'],
- incomingSuperRoles => array[
- hsOfficeRelationAdmin(NEW),
- hsOfficePersonAdmin(newAnchor),
- hsOfficePersonAdmin(newHolder),
- hsOfficeContactAdmin(newContact)],
- outgoingSubRoles => array[
- hsOfficePersonTenant(newAnchor),
- hsOfficePersonTenant(newHolder),
- hsOfficeContactTenant(newContact)]
- );
+ perform createRoleWithGrants(
+ hsOfficeRelationAdmin(NEW),
+ permissions => array['UPDATE'],
+ incomingSuperRoles => array[
+ hsOfficePersonAdmin(newAnchorPerson),
+ hsOfficeRelationOwner(NEW)]
+ );
- -- anchor and holder admin roles need each others tenant role
- -- to be able to see the joined relation
- -- TODO: this can probably be avoided through agent+guest roles
- call grantRoleToRole(hsOfficePersonTenant(newAnchor), hsOfficePersonAdmin(newHolder));
- call grantRoleToRole(hsOfficePersonTenant(newHolder), hsOfficePersonAdmin(newAnchor));
- call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newHolder), hsOfficeContactAdmin(newContact));
+ perform createRoleWithGrants(
+ hsOfficeRelationAgent(NEW),
+ incomingSuperRoles => array[
+ hsOfficePersonAdmin(newHolderPerson),
+ hsOfficeRelationAdmin(NEW)]
+ );
- elsif TG_OP = 'UPDATE' then
-
- if OLD.contactUuid <> NEW.contactUuid then
- -- nothing but the contact can be updated,
- -- in other cases, a new relation needs to be created and the old updated
-
- select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
-
- call revokeRoleFromRole( hsOfficeRelationTenant, hsOfficeContactAdmin(oldContact) );
- call grantRoleToRole( hsOfficeRelationTenant, hsOfficeContactAdmin(newContact) );
-
- call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationTenant );
- call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationTenant );
- end if;
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ 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);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_relation row.
*/
-create trigger createRbacRolesForHsOfficeRelation_Trigger
- after insert
- on hs_office_relation
- for each row
-execute procedure hsOfficeRelationRbacRolesTrigger();
-/*
- An AFTER UPDATE TRIGGER which updates the role structure of a customer.
- */
-create trigger updateRbacRolesForHsOfficeRelation_Trigger
- after update
- on hs_office_relation
+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 hsOfficeRelationRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficeRelation_tf();
--//
-- ============================================================================
---changeset hs-office-relation-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-relation-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_relation', $idName$
- (select idName from hs_office_person_iv p where p.uuid = target.anchorUuid)
- || '-with-' || target.type || '-' ||
- (select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
- $idName$);
+
+/*
+ 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',
- '(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)',
+ $orderBy$
+ (select idName from hs_office_person_iv p where p.uuid = target.holderUuid)
+ $orderBy$,
$updates$
contactUuid = new.contactUuid
$updates$);
--//
--- TODO: exception if one tries to amend any other column
-
-
--- ============================================================================
---changeset hs-office-relation-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-relation and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-relation permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relation']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeRelationNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-relation 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_relation_insert_trigger
- before insert
- on hs_office_relation
- for each row
- -- TODO.spec: who is allowed to create new relations
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeRelationNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
index 8ad39359..9bdcab18 100644
--- a/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
+++ b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
@@ -11,7 +11,7 @@
create or replace procedure createHsOfficeRelationTestData(
holderPersonName varchar,
relationType HsOfficeRelationType,
- anchorPersonTradeName varchar,
+ anchorPersonName varchar,
contactLabel varchar,
mark varchar default null)
language plpgsql as $$
@@ -23,24 +23,28 @@ declare
contact hs_office_contact;
begin
- idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName);
+ 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.* from hs_office_person p where p.tradeName = anchorPersonTradeName into anchorPerson;
+ 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', anchorPersonTradeName;
+ raise exception 'anchorPerson "%" not found', anchorPersonName;
end if;
- select p.* from hs_office_person p
- where p.tradeName = holderPersonName or p.familyName = holderPersonName
- into holderPerson;
+ 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.* from hs_office_contact c where c.label = contactLabel into contact;
+ 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;
@@ -87,17 +91,22 @@ 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;
$$;
diff --git a/src/main/resources/db/changelog/230-hs-office-partner.sql b/src/main/resources/db/changelog/230-hs-office-partner.sql
index 73a02fa1..d02ed017 100644
--- a/src/main/resources/db/changelog/230-hs-office-partner.sql
+++ b/src/main/resources/db/changelog/230-hs-office-partner.sql
@@ -33,23 +33,20 @@ 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), -- TODO: delete in after delete trigger
- personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRelUuid
- contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRelUuid
+ 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-DETAILS-TRIGGER:1 endDelimiter:--//
+--changeset hs-office-partner-DELETE-DEPENDENTS-TRIGGER:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-
/**
Trigger function to delete related details of a partner to delete.
*/
-create or replace function deleteHsOfficeDetailsOnPartnerDelete()
+create or replace function deleteHsOfficeDependentsOnPartnerDelete()
returns trigger
language PLPGSQL
as $$
@@ -61,17 +58,24 @@ begin
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 details of a partner to delete.
+ Triggers deletion of related rows of a partner to delete.
*/
-create trigger hs_office_partner_delete_details_trigger
+create trigger hs_office_partner_delete_dependents_trigger
after delete
on hs_office_partner
for each row
- execute procedure deleteHsOfficeDetailsOnPartnerDelete();
+ execute procedure deleteHsOfficeDependentsOnPartnerDelete();
-- ============================================================================
--changeset hs-office-partner-MAIN-TABLE-JOURNAL:1 endDelimiter:--//
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
deleted file mode 100644
index 98bd276d..00000000
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.md
+++ /dev/null
@@ -1,158 +0,0 @@
-### 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.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 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
-
- 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
-
-```
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
deleted file mode 100644
index 8b12e95f..00000000
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac-generated.sql
+++ /dev/null
@@ -1,248 +0,0 @@
---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;
- permissionUuid uuid;
- roleUuid uuid;
- begin
- call defineContext('create INSERT INTO hs_office_partner permissions for the related global rows');
-
- FOR row IN SELECT * FROM global
- LOOP
- roleUuid := findRoleId(globalAdmin());
- permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner');
- call grantPermissionToRole(permissionUuid, roleUuid);
- 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 generateRbacIdentityViewFromQuery('hs_office_partner',
- $idName$
- SELECT partner.partnerNumber
- || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
- || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
- FROM hs_office_partner AS partner
- $idName$);
---//
-
--- ============================================================================
---changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_partner',
- $orderBy$
- SELECT partner.partnerNumber
- || ':' || (SELECT idName FROM hs_office_person_iv p WHERE p.uuid = partner.personUuid)
- || '-' || (SELECT idName FROM hs_office_contact_iv c WHERE c.uuid = partner.contactUuid)
- FROM hs_office_partner AS partner
- $orderBy$,
- $updates$
- partnerRelUuid = new.partnerRelUuid,
- personUuid = new.personUuid,
- contactUuid = new.contactUuid
- $updates$);
---//
-
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
index 148343c3..98bd276d 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
@@ -1,78 +1,158 @@
-### hs_office_partner RBAC
+### rbac partner
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill:#eee
-
- role:global.admin[global.admin]
-end
-
-subgraph hsOfficeContact
+subgraph partnerRel.contact["`**partnerRel.contact**`"]
direction TB
- style hsOfficeContact fill:#eee
-
- role:hsOfficeContact.admin[contact.admin]
- --> role:hsOfficeContact.tenant[contact.tenant]
- --> role:hsOfficeContact.guest[contact.guest]
+ 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 hsOfficePerson
+subgraph partner["`**partner**`"]
direction TB
- style hsOfficePerson fill:#eee
-
- role:hsOfficePerson.admin[person.admin]
- --> role:hsOfficePerson.tenant[person.tenant]
- --> role:hsOfficePerson.guest[person.guest]
+ 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.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 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
+
+ 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 hsOfficePartnerDetails
+subgraph partnerDetails["`**partnerDetails**`"]
direction TB
-
- perm:hsOfficePartnerDetails.*{{partner.*}}
- perm:hsOfficePartnerDetails.edit{{partner.edit}}
- perm:hsOfficePartnerDetails.view{{partner.view}}
+ 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 hsOfficePartner
-
- role:hsOfficePartner.owner[partner.owner]
- %% permissions
- role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
- role:hsOfficePartner.owner --> perm:hsOfficePartnerDetails.*{{partner.*}}
- %% incoming
- role:global.admin ---> role:hsOfficePartner.owner
-
- role:hsOfficePartner.admin[partner.admin]
- %% permissions
- role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
- role:hsOfficePartner.admin --> perm:hsOfficePartnerDetails.edit{{partner.edit}}
- %% incoming
- role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
- %% outgoing
- role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
- role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
-
- role:hsOfficePartner.agent[partner.agent]
- %% permissions
- role:hsOfficePartner.agent --> perm:hsOfficePartnerDetails.view{{partner.view}}
- %% incoming
- role:hsOfficePartner.admin ---> role:hsOfficePartner.agent
- role:hsOfficePerson.admin --> role:hsOfficePartner.agent
- role:hsOfficeContact.admin --> role:hsOfficePartner.agent
-
- role:hsOfficePartner.tenant[partner.tenant]
- %% incoming
- role:hsOfficePartner.agent --> role:hsOfficePartner.tenant
- %% outgoing
- role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
- role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
+subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
+ direction TB
+ style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- role:hsOfficePartner.guest[partner.guest]
- %% permissions
- role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
- %% incoming
- role:hsOfficePartner.tenant --> role:hsOfficePartner.guest
+ 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
+
```
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
index c2882dbb..9cdd92fc 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-partner-rbac-OBJECT:1 endDelimiter:--//
@@ -15,242 +17,222 @@ call generateRbacRoleDescriptors('hsOfficePartner', 'hs_office_partner');
-- ============================================================================
---changeset hs-office-partner-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-partner-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for partner entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficePartnerRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficePartner(
+ NEW hs_office_partner
+)
+ language plpgsql as $$
+
declare
- oldPartnerRel hs_office_relation;
- newPartnerRel hs_office_relation;
+ newPartnerRel hs_office_relation;
+ newPartnerDetails hs_office_partner_details;
- oldPerson hs_office_person;
- newPerson hs_office_person;
-
- oldContact hs_office_contact;
- newContact hs_office_contact;
begin
call enterTriggerForObjectUuid(NEW.uuid);
- select * from hs_office_relation as r where r.uuid = NEW.partnerReluuid into newPartnerRel;
- select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
- select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
+ 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);
- if TG_OP = 'INSERT' then
+ 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);
- -- === ATTENTION: code generated from related Mermaid flowchart: ===
-
- perform createRoleWithGrants(
- hsOfficePartnerOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()]
- );
-
- perform createRoleWithGrants(
- hsOfficePartnerAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[
- hsOfficePartnerOwner(NEW)],
- outgoingSubRoles => array[
- hsOfficeRelationTenant(newPartnerRel),
- hsOfficePersonTenant(newPerson),
- hsOfficeContactTenant(newContact)]
- );
-
- perform createRoleWithGrants(
- hsOfficePartnerAgent(NEW),
- incomingSuperRoles => array[
- hsOfficePartnerAdmin(NEW),
- hsOfficeRelationAdmin(newPartnerRel),
- hsOfficePersonAdmin(newPerson),
- hsOfficeContactAdmin(newContact)]
- );
-
- perform createRoleWithGrants(
- hsOfficePartnerTenant(NEW),
- incomingSuperRoles => array[
- hsOfficePartnerAgent(NEW)],
- outgoingSubRoles => array[
- hsOfficeRelationTenant(newPartnerRel),
- hsOfficePersonGuest(newPerson),
- hsOfficeContactGuest(newContact)]
- );
-
- perform createRoleWithGrants(
- hsOfficePartnerGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
- );
-
- -- === END of code generated from Mermaid flowchart. ===
-
- -- Each partner-details entity belong exactly to one partner entity
- -- and it makes little sense just to delegate partner-details roles.
- -- Therefore, we did not model partner-details roles,
- -- but instead just assign extra permissions to existing partner-roles.
-
- --Attention: Cannot be in partner-details because of insert order (partner is not in database yet)
-
- call grantPermissionsToRole(
- getRoleId(hsOfficePartnerOwner(NEW)),
- createPermissions(NEW.detailsUuid, array ['DELETE'])
- );
-
- call grantPermissionsToRole(
- getRoleId(hsOfficePartnerAdmin(NEW)),
- createPermissions(NEW.detailsUuid, array ['UPDATE'])
- );
-
- call grantPermissionsToRole(
- -- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
- -- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
- -- Otherwise package-admins etc. would be able to read the data.
- getRoleId(hsOfficePartnerAgent(NEW)),
- createPermissions(NEW.detailsUuid, array ['SELECT'])
- );
-
-
- elsif TG_OP = 'UPDATE' then
-
- if OLD.partnerRelUuid <> NEW.partnerRelUuid then
- select * from hs_office_relation as r where r.uuid = OLD.partnerRelUuid into oldPartnerRel;
-
- call revokeRoleFromRole(hsOfficeRelationTenant(oldPartnerRel), hsOfficePartnerAdmin(OLD));
- call grantRoleToRole(hsOfficeRelationTenant(newPartnerRel), hsOfficePartnerAdmin(NEW));
-
- call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationAdmin(oldPartnerRel));
- call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationAdmin(newPartnerRel));
-
- call revokeRoleFromRole(hsOfficeRelationGuest(oldPartnerRel), hsOfficePartnerTenant(OLD));
- call grantRoleToRole(hsOfficeRelationGuest(newPartnerRel), hsOfficePartnerTenant(NEW));
- end if;
-
- if OLD.personUuid <> NEW.personUuid then
- select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
-
- call revokeRoleFromRole(hsOfficePersonTenant(oldPerson), hsOfficePartnerAdmin(OLD));
- call grantRoleToRole(hsOfficePersonTenant(newPerson), hsOfficePartnerAdmin(NEW));
-
- call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficePersonAdmin(oldPerson));
- call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficePersonAdmin(newPerson));
-
- call revokeRoleFromRole(hsOfficePersonGuest(oldPerson), hsOfficePartnerTenant(OLD));
- call grantRoleToRole(hsOfficePersonGuest(newPerson), hsOfficePartnerTenant(NEW));
- end if;
-
- if OLD.contactUuid <> NEW.contactUuid then
- select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
-
- call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficePartnerAdmin(OLD));
- call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficePartnerAdmin(NEW));
-
- call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeContactAdmin(oldContact));
- call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeContactAdmin(newContact));
-
- call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficePartnerTenant(OLD));
- call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficePartnerTenant(NEW));
- end if;
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ 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);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_partner row.
*/
-create trigger createRbacRolesForHsOfficePartner_Trigger
- after insert
- on hs_office_partner
- for each row
-execute procedure hsOfficePartnerRbacRolesTrigger();
-/*
- An AFTER UPDATE TRIGGER which updates the role structure of a customer.
- */
-create trigger updateRbacRolesForHsOfficePartner_Trigger
- after update
- on hs_office_partner
+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 hsOfficePartnerRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficePartner_tf();
--//
-- ============================================================================
---changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-partner-rbac-update-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
- partnerNumber || ':' ||
- (select idName from hs_office_person_iv p where p.uuid = target.personuuid)
- || '-' ||
- (select idName from hs_office_contact_iv c where c.uuid = target.contactuuid)
- $idName$);
+
+/*
+ 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',
- '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
+ $orderBy$
+ 'P-' || partnerNumber
+ $orderBy$,
$updates$
- partnerRelUuid = new.partnerRelUuid,
- personUuid = new.personUuid,
- contactUuid = new.contactUuid
+ partnerRelUuid = new.partnerRelUuid
$updates$);
--//
-
--- ============================================================================
---changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-partner and assigns it to the Hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-partner permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-partner']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficePartnerNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-partner 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_partner_insert_trigger
- before insert
- on hs_office_partner
- for each row
- -- TODO.spec: who is allowed to create new partners
- when ( not hasAssumedRole() )
-execute procedure addHsOfficePartnerNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
deleted file mode 100644
index ece32f9c..00000000
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.md
+++ /dev/null
@@ -1,136 +0,0 @@
-### rbac partnerDetails
-
-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 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
-
- subgraph partnerRel["`**partnerRel**`"]
- direction TB
- style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
- 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 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:partnerDetails:INSERT
-
-```
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
deleted file mode 100644
index 4fd78a87..00000000
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac-generated.sql
+++ /dev/null
@@ -1,164 +0,0 @@
---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
- newPartnerRel hs_office_relation;
-
-begin
- call enterTriggerForObjectUuid(NEW.uuid);
-
- SELECT partnerRel.*
- FROM hs_office_relation AS partnerRel
- JOIN hs_office_partner AS partner
- ON partner.detailsUuid = NEW.uuid
- WHERE partnerRel.uuid = partner.partnerRelUuid
- INTO newPartnerRel;
- assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
-
- 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;
- permissionUuid uuid;
- roleUuid uuid;
- begin
- call defineContext('create INSERT INTO hs_office_partner_details permissions for the related global rows');
-
- FOR row IN SELECT * FROM global
- LOOP
- roleUuid := findRoleId(globalAdmin());
- permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_partner_details');
- call grantPermissionToRole(permissionUuid, roleUuid);
- 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 partner_iv.idName || '-details'
- 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$
- SELECT partner_iv.idName || '-details'
- 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
- $orderBy$,
- $updates$
- registrationOffice = new.registrationOffice,
- registrationNumber = new.registrationNumber,
- birthPlace = new.birthPlace,
- birthName = new.birthName,
- birthday = new.birthday,
- dateOfDeath = new.dateOfDeath
- $updates$);
---//
-
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
new file mode 100644
index 00000000..d27a1064
--- /dev/null
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
@@ -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
+
+```
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
index c4e053b9..a594823b 100644
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-partner-details-rbac-OBJECT:1 endDelimiter:--//
@@ -8,76 +10,141 @@ call generateRelatedRbacObject('hs_office_partner_details');
-- ============================================================================
---changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-partner-details-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_partner_details', $idName$
- (select idName || '-details' from hs_office_partner_iv partner_iv
- join hs_office_partner partner on (partner_iv.uuid = partner.uuid)
- where partner.detailsUuid = target.uuid)
- $idName$);
+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 % (%) assumed by user % (%)',
+ currentSubjects(), currentSubjectsUuids(), currentUser(), currentUserUuid();
+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 || '-details' 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',
- 'target.uuid', -- no specific order required
+ $orderBy$
+ uuid
+ $orderBy$,
$updates$
registrationOffice = new.registrationOffice,
registrationNumber = new.registrationNumber,
- birthPlace = new.birthPlace,
- birthName = new.birthName,
- birthday = new.birthday,
- dateOfDeath = new.dateOfDeath
+ birthPlace = new.birthPlace,
+ birthName = new.birthName,
+ birthday = new.birthday,
+ dateOfDeath = new.dateOfDeath
$updates$);
--//
-
--- ============================================================================
---changeset hs-office-partner-details-rbac-NEW-PARTNER-DETAILS:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-partner-details permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-partner-details']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
--- TODO.refa: the code below could be moved to a generator, maybe even the code above.
--- Additionally, the code below is not neccesary for all entities, specifiy when it is!
-
-/**
- Used by the trigger to prevent the add-partner-details to current user respectively assumed roles.
- */
-create or replace function addHsOfficePartnerDetailsNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-partner-details not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
-end; $$;
-
-/**
- Checks if the user or assumed roles are allowed to create new partner-details.
- */
-create trigger hs_office_partner_details_insert_trigger
- before insert
- on hs_office_partner_details
- for each row
- when ( not hasAssumedRole() )
-execute procedure addHsOfficePartnerDetailsNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
index 765803a8..ae3ed66e 100644
--- a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
+++ b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
@@ -9,18 +9,17 @@
Creates a single partner test record.
*/
create or replace procedure createHsOfficePartnerTestData(
- mandantTradeName varchar,
- partnerNumber numeric(5),
+ mandantTradeName varchar,
+ newPartnerNumber numeric(5),
partnerPersonName varchar,
- contactLabel varchar )
+ contactLabel varchar )
language plpgsql as $$
declare
currentTask varchar;
idName varchar;
mandantPerson hs_office_person;
- partnerRel hs_office_relation;
+ partnerRel hs_office_relation;
relatedPerson hs_office_person;
- relatedContact hs_office_contact;
relatedDetailsUuid uuid;
begin
idName := cleanIdentifier( partnerPersonName|| '-' || contactLabel);
@@ -38,9 +37,6 @@ begin
select p.* from hs_office_person p
where p.tradeName = partnerPersonName or p.familyName = partnerPersonName
into relatedPerson;
- select c.* from hs_office_contact c
- where c.label = contactLabel
- into relatedContact;
select r.* from hs_office_relation r
where r.type = 'PARTNER'
@@ -53,7 +49,6 @@ begin
raise notice 'creating test partner: %', idName;
raise notice '- using partnerRel (%): %', partnerRel.uuid, partnerRel;
raise notice '- using person (%): %', relatedPerson.uuid, relatedPerson;
- raise notice '- using contact (%): %', relatedContact.uuid, relatedContact;
if relatedPerson.persontype = 'NP' then
insert
@@ -68,8 +63,8 @@ begin
end if;
insert
- into hs_office_partner (uuid, partnerNumber, partnerRelUuid, personuuid, contactuuid, detailsUuid)
- values (uuid_generate_v4(), partnerNumber, partnerRel.uuid, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
+ into hs_office_partner (uuid, partnerNumber, partnerRelUuid, detailsUuid)
+ values (uuid_generate_v4(), newPartnerNumber, partnerRel.uuid, relatedDetailsUuid);
end; $$;
--//
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
deleted file mode 100644
index 4f1604fb..00000000
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.md
+++ /dev/null
@@ -1,43 +0,0 @@
-### 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: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:bankAccount:owner ==> perm:bankAccount:DELETE
-role:bankAccount:admin ==> perm:bankAccount:UPDATE
-role:bankAccount:referrer ==> perm:bankAccount:SELECT
-
-```
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
deleted file mode 100644
index 6b96fb34..00000000
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac-generated.sql
+++ /dev/null
@@ -1,125 +0,0 @@
---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:--//
--- ----------------------------------------------------------------------------
-
-/**
- Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount,
- where only global-admin has that permission.
-*/
-create or replace function hs_office_bankaccount_insert_permission_missing_tf()
- returns trigger
- language plpgsql as $$
-begin
- raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)',
- currentSubjects(), currentSubjectsUuids();
-end; $$;
-
-create trigger hs_office_bankaccount_insert_permission_check_tg
- before insert on hs_office_bankaccount
- for each row
- when ( not isGlobalAdmin() )
- execute procedure hs_office_bankaccount_insert_permission_missing_tf();
---//
-
--- ============================================================================
---changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-
-call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
- $idName$
- iban || ':' || holder
- $idName$);
---//
-
--- ============================================================================
---changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_bankaccount',
- $orderBy$
- iban || ':' || holder
- $orderBy$,
- $updates$
- holder = new.holder,
- iban = new.iban,
- bic = new.bic
- $updates$);
---//
-
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
index b2cee782..c33e3374 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
@@ -1,40 +1,45 @@
-### hs_office_bankaccount RBAC Roles
+### rbac bankAccount
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill: lightgray
-
- role:global.admin[global.admin]
-end
-
-subgraph hsOfficeBankAccount
+subgraph bankAccount["`**bankAccount**`"]
direction TB
- style hsOfficeBankAccount fill: lightgreen
-
- user:hsOfficeBankAccount.creator([bankAccount.creator])
+ style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
- role:hsOfficeBankAccount.owner[[bankAccount.owner]]
- %% permissions
- role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{hsOfficeBankAccount.delete}}
- %% incoming
- role:global.admin --> role:hsOfficeBankAccount.owner
- user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
-
- role:hsOfficeBankAccount.admin[[bankAccount.admin]]
- %% incoming
- role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
-
- role:hsOfficeBankAccount.tenant[[bankAccount.tenant]]
- %% incoming
- role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
-
- role:hsOfficeBankAccount.guest[[bankAccount.guest]]
- %% permissions
- role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{hsOfficeBankAccount.view}}
- %% incoming
- role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
+ 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
+
+```
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
index 93b605ce..c4628183 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
@@ -15,125 +17,129 @@ call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount')
-- ============================================================================
---changeset hs-office-bankaccount-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates the roles and their assignments for a new bankaccount for the AFTER INSERT TRIGGER.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function createRbacRolesForHsOfficeBankAccount()
+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
- if TG_OP <> 'INSERT' then
- raise exception 'invalid usage of TRIGGER AFTER INSERT';
- end if;
-
- perform createRoleWithGrants(
- hsOfficeBankAccountOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
- userUuids => array[currentUserUuid()],
- grantedByRole => globalAdmin()
- );
-
- perform createRoleWithGrants(
- hsOfficeBankAccountAdmin(NEW),
- incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeBankAccountTenant(NEW),
- incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeBankAccountGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
- );
-
+ call buildRbacSystemForHsOfficeBankAccount(NEW);
return NEW;
end; $$;
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-
-create trigger createRbacRolesForHsOfficeBankAccount_Trigger
- after insert
- on hs_office_bankaccount
+create trigger insertTriggerForHsOfficeBankAccount_tg
+ after insert on hs_office_bankaccount
for each row
-execute procedure createRbacRolesForHsOfficeBankAccount();
+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$
- target.holder
+call generateRbacIdentityViewFromProjection('hs_office_bankaccount',
+ $idName$
+ iban
$idName$);
--//
-
-- ============================================================================
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_bankaccount', 'target.holder',
+call generateRbacRestrictedView('hs_office_bankaccount',
+ $orderBy$
+ iban
+ $orderBy$,
$updates$
holder = new.holder,
iban = new.iban,
bic = new.bic
$updates$);
---/
-
-
--- ============================================================================
---changeset hs-office-bankaccount-rbac-NEW-BANKACCOUNT:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-bankaccount and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-bankaccount permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-bankaccount']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeBankAccountNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-bankaccount 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_bankaccount_insert_trigger
- before insert
- on hs_office_bankaccount
- for each row
- -- TODO.spec: who is allowed to create new bankaccounts
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeBankAccountNotAllowedForCurrentSubjects();
--//
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
deleted file mode 100644
index f542e78c..00000000
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.md
+++ /dev/null
@@ -1,178 +0,0 @@
-### 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}}
- end
-end
-
-subgraph debitorRel["`**debitorRel**`"]
- direction TB
- style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
-
- 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 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
-
-```
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
deleted file mode 100644
index 1e383951..00000000
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac-generated.sql
+++ /dev/null
@@ -1,143 +0,0 @@
---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;
-
- SELECT * FROM hs_office_relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
-
- 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:--//
--- ----------------------------------------------------------------------------
-
-/**
- Checks if the user or assumed roles are allowed to insert a row to hs_office_sepamandate,
- where only global-admin has that permission.
-*/
-create or replace function hs_office_sepamandate_insert_permission_missing_tf()
- returns trigger
- language plpgsql as $$
-begin
- raise exception '[403] insert into hs_office_sepamandate not allowed for current subjects % (%)',
- currentSubjects(), currentSubjectsUuids();
-end; $$;
-
-create trigger hs_office_sepamandate_insert_permission_check_tg
- before insert on hs_office_sepamandate
- for each row
- when ( not isGlobalAdmin() )
- execute procedure hs_office_sepamandate_insert_permission_missing_tf();
---//
-
--- ============================================================================
---changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-
-call generateRbacIdentityViewFromProjection('hs_office_sepamandate',
- $idName$
- concat(tradeName, familyName, givenName)
- $idName$);
---//
-
--- ============================================================================
---changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_sepamandate',
- $orderBy$
- concat(tradeName, familyName, givenName)
- $orderBy$,
- $updates$
- reference = new.reference,
- agreement = new.agreement,
- validity = new.validity
- $updates$);
---//
-
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
index 78bb7751..43fb6ef3 100644
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
@@ -1,71 +1,180 @@
-### hs_office_sepaMandate RBAC
+### rbac sepaMandate
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill:#eee
-
- role:global.admin[global.admin]
-end
-
-subgraph hsOfficeBankAccount
+subgraph bankAccount["`**bankAccount**`"]
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]
+ 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 hsOfficeDebitor
+subgraph debitorRel.contact["`**debitorRel.contact**`"]
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]
+ 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 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
+subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
+ direction TB
+ style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- role:hsOfficeSepaMandate.guest[sepaMandate.guest]
- %% permissions
- role:hsOfficeSepaMandate.guest --> perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
- %% incoming
- role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
+ 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.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 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
```
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
index da7887cd..0f168fd5 100644
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
@@ -15,144 +17,191 @@ call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate')
-- ============================================================================
---changeset hs-office-sepamandate-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for sepaMandate entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeSepaMandateRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
+ NEW hs_office_sepamandate
+)
+ language plpgsql as $$
+
declare
- newHsOfficeDebitor hs_office_debitor;
- newHsOfficeBankAccount hs_office_bankAccount;
+ newBankAccount hs_office_bankaccount;
+ newDebitorRel hs_office_relation;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- 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;
+ 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);
- if TG_OP = 'INSERT' then
+ 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);
- -- === ATTENTION: code generated from related Mermaid flowchart: ===
- perform createRoleWithGrants(
- hsOfficeSepaMandateOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()]
- );
+ perform createRoleWithGrants(
+ hsOfficeSepaMandateOwner(NEW),
+ permissions => array['DELETE'],
+ incomingSuperRoles => array[globalAdmin()],
+ userUuids => array[currentUserUuid()]
+ );
- perform createRoleWithGrants(
- hsOfficeSepaMandateAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
- outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
- );
+ perform createRoleWithGrants(
+ hsOfficeSepaMandateAdmin(NEW),
+ permissions => array['UPDATE'],
+ incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
+ );
- perform createRoleWithGrants(
- hsOfficeSepaMandateAgent(NEW),
- incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW), hsOfficeDebitorAdmin(newHsOfficeDebitor), hsOfficeBankAccountAdmin(newHsOfficeBankAccount)],
- outgoingSubRoles => array[hsOfficeDebitorTenant(newHsOfficeDebitor)]
- );
+ perform createRoleWithGrants(
+ hsOfficeSepaMandateAgent(NEW),
+ incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
+ outgoingSubRoles => array[
+ hsOfficeBankAccountReferrer(newBankAccount),
+ hsOfficeRelationAgent(newDebitorRel)]
+ );
- perform createRoleWithGrants(
- hsOfficeSepaMandateTenant(NEW),
- incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)],
- outgoingSubRoles => array[hsOfficeDebitorGuest(newHsOfficeDebitor), hsOfficeBankAccountGuest(newHsOfficeBankAccount)]
- );
-
- perform createRoleWithGrants(
- hsOfficeSepaMandateGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
- );
-
- -- === END of code generated from Mermaid flowchart. ===
-
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ perform createRoleWithGrants(
+ hsOfficeSepaMandateReferrer(NEW),
+ permissions => array['SELECT'],
+ incomingSuperRoles => array[
+ hsOfficeBankAccountAdmin(newBankAccount),
+ hsOfficeRelationAgent(newDebitorRel),
+ hsOfficeSepaMandateAgent(NEW)],
+ outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
+ );
call leaveTriggerForObjectUuid(NEW.uuid);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_sepamandate row.
*/
-create trigger createRbacRolesForHsOfficeSepaMandate_Trigger
- after insert
- on hs_office_sepamandate
+
+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 hsOfficeSepaMandateRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficeSepaMandate_tf();
--//
-- ============================================================================
---changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-sepamandate-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_sepamandate', 'target.reference');
+
+/*
+ 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 => 'target.reference',
- columnUpdates => $updates$
+ $orderBy$
+ validity
+ $orderBy$,
+ $updates$
reference = new.reference,
agreement = new.agreement,
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
index eb96d1a0..11999980 100644
--- 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
@@ -8,31 +8,36 @@
/*
Creates a single sepaMandate test record.
*/
-create or replace procedure createHsOfficeSepaMandateTestData( tradeNameAndHolderName varchar )
+create or replace procedure createHsOfficeSepaMandateTestData(
+ forPartnerNumber numeric(5),
+ forDebitorSuffix numeric(2),
+ forIban varchar,
+ withReference 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;
+ 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.* 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;
+ 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: %', idName;
+ 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, 'ref'||idName, '20220930', daterange('20221001' , '20261231', '[]'));
+ values (uuid_generate_v4(), relatedDebitor.uuid, relatedBankAccount.uuid, withReference, '20220930', daterange('20221001' , '20261231', '[]'));
end; $$;
--//
@@ -43,9 +48,9 @@ end; $$;
do language plpgsql $$
begin
- call createHsOfficeSepaMandateTestData('First GmbH');
- call createHsOfficeSepaMandateTestData('Second e.K.');
- call createHsOfficeSepaMandateTestData('Third OHG');
+ 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;
$$;
--//
diff --git a/src/main/resources/db/changelog/270-hs-office-debitor.sql b/src/main/resources/db/changelog/270-hs-office-debitor.sql
index fae4e90c..e2174eca 100644
--- a/src/main/resources/db/changelog/270-hs-office-debitor.sql
+++ b/src/main/resources/db/changelog/270-hs-office-debitor.sql
@@ -7,10 +7,9 @@
create table hs_office_debitor
(
uuid uuid unique references RbacObject (uuid) initially deferred,
- partnerUuid uuid not null references hs_office_partner(uuid),
- billable boolean not null default true,
debitorNumberSuffix numeric(2) not null,
- billingContactUuid uuid not null references hs_office_contact(uuid),
+ 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,
@@ -20,11 +19,43 @@ create table hs_office_debitor
constraint check_default_prefix check (
defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$'
)
- -- TODO.impl: SEPA-mandate
);
--//
+-- ============================================================================
+--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:--//
-- ----------------------------------------------------------------------------
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
deleted file mode 100644
index a1baa702..00000000
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.md
+++ /dev/null
@@ -1,275 +0,0 @@
-### 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.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 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: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.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 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 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: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
-
-```
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
deleted file mode 100644
index f827ea67..00000000
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac-generated.sql
+++ /dev/null
@@ -1,231 +0,0 @@
---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 * FROM hs_office_relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
-
- 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.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;
- permissionUuid uuid;
- roleUuid uuid;
- begin
- call defineContext('create INSERT INTO hs_office_debitor permissions for the related global rows');
-
- FOR row IN SELECT * FROM global
- LOOP
- roleUuid := findRoleId(globalAdmin());
- permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_debitor');
- call grantPermissionToRole(permissionUuid, roleUuid);
- 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,
- '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 partnerRel.type = 'DEBITOR'
- WHERE debitorRel.uuid = debitor.debitorRelUuid)
- || to_char(debitorNumberSuffix, 'fm00')
- from hs_office_debitor as debitor
- $idName$);
---//
-
--- ============================================================================
---changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_debitor',
- $orderBy$
- SELECT debitor.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 partnerRel.type = 'DEBITOR'
- WHERE debitorRel.uuid = debitor.debitorRelUuid)
- || to_char(debitorNumberSuffix, 'fm00')
- from hs_office_debitor as debitor
- $orderBy$,
- $updates$
- debitorRel = new.debitorRel,
- billable = new.billable,
- debitorUuid = new.debitorUuid,
- refundBankAccountUuid = new.refundBankAccountUuid,
- vatId = new.vatId,
- vatCountryCode = new.vatCountryCode,
- vatBusiness = new.vatBusiness,
- vatReverseCharge = new.vatReverseCharge,
- defaultPrefix = new.defaultPrefix
- $updates$);
---//
-
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 6830a7b1..a1baa702 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
@@ -1,250 +1,275 @@
-### hs_office_debitor RBAC Roles
+### rbac debitor
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill:#eee
-
- role:global.admin[global.admin]
-end
+subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
+ direction TB
+ style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
-subgraph office
- style office fill:#eee
-
- subgraph sepa
-
- subgraph bankaccount
- style bankaccount fill: #e9f7ef
-
- user:hsOfficeBankAccount.creator([bankaccount.creator])
-
- role:hsOfficeBankAccount.owner[bankaccount.owner]
- %% permissions
- role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{bankaccount.*}}
- %% incoming
- role:global.admin --> role:hsOfficeBankAccount.owner
- user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
-
- role:hsOfficeBankAccount.admin[bankaccount.admin]
- %% permissions
- role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{bankaccount.edit}}
- %% incoming
- role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
-
- role:hsOfficeBankAccount.tenant[bankaccount.tenant]
- %% incoming
- role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
-
- role:hsOfficeBankAccount.guest[bankaccount.guest]
- %% permissions
- role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{bankaccount.view}}
- %% incoming
- role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
- end
-
- subgraph hsOfficeSepaMandate
- end
-
- end
-
- subgraph contact
- style contact fill: #e9f7ef
-
- user:hsOfficeContact.creator([contact.creator])
-
- role:hsOfficeContact.owner[contact.owner]
- %% permissions
- role:hsOfficeContact.owner --> perm:hsOfficeContact.*{{contact.*}}
- %% incoming
- role:global.admin --> role:hsOfficeContact.owner
- user:hsOfficeContact.creator ---> role:hsOfficeContact.owner
-
- role:hsOfficeContact.admin[contact.admin]
- %% permissions
- role:hsOfficeContact.admin ---> perm:hsOfficeContact.edit{{contact.edit}}
- %% incoming
- role:hsOfficeContact.owner ---> role:hsOfficeContact.admin
-
- role:hsOfficeContact.tenant[contact.tenant]
- %% incoming
- role:hsOfficeContact.admin ----> role:hsOfficeContact.tenant
-
- role:hsOfficeContact.guest[contact.guest]
- %% permissions
- role:hsOfficeContact.guest --> perm:hsOfficeContact.view{{contact.view}}
- %% incoming
- role:hsOfficeContact.tenant ---> role:hsOfficeContact.guest
- end
-
- subgraph partner-person
-
- subgraph person
- style person fill: #e9f7ef
-
- user:hsOfficePerson.creator([personcreator])
-
- role:hsOfficePerson.owner[person.owner]
- %% permissions
- role:hsOfficePerson.owner --> perm:hsOfficePerson.*{{person.*}}
- %% incoming
- user:hsOfficePerson.creator ---> role:hsOfficePerson.owner
- role:global.admin --> role:hsOfficePerson.owner
-
- role:hsOfficePerson.admin[person.admin]
- %% permissions
- role:hsOfficePerson.admin --> perm:hsOfficePerson.edit{{person.edit}}
- %% incoming
- role:hsOfficePerson.owner ---> role:hsOfficePerson.admin
-
- role:hsOfficePerson.tenant[person.tenant]
- %% incoming
- role:hsOfficePerson.admin -----> role:hsOfficePerson.tenant
-
- role:hsOfficePerson.guest[person.guest]
- %% permissions
- role:hsOfficePerson.guest --> perm:hsOfficePerson.edit{{person.view}}
- %% incoming
- role:hsOfficePerson.tenant ---> role:hsOfficePerson.guest
- end
-
- subgraph partner
-
- role:hsOfficePartner.owner[partner.owner]
- %% permissions
- role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
- %% incoming
- role:global.admin ---> role:hsOfficePartner.owner
-
- role:hsOfficePartner.admin[partner.admin]
- %% permissions
- role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
- %% incoming
- role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
- %% outgoing
- role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
- role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
-
- role:hsOfficePartner.agent[partner.agent]
- %% incoming
- role:hsOfficePartner.admin --> role:hsOfficePartner.agent
- role:hsOfficePerson.admin --> role:hsOfficePartner.agent
- role:hsOfficeContact.admin --> role:hsOfficePartner.agent
-
- role:hsOfficePartner.tenant[partner.tenant]
- %% incoming
- role:hsOfficePartner.agent ---> role:hsOfficePartner.tenant
- %% outgoing
- role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
- role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
-
- role:hsOfficePartner.guest[partner.guest]
- %% permissions
- role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
- %% incoming
- role:hsOfficePartner.tenant ---> role:hsOfficePartner.guest
- end
-
- end
-
- subgraph debitor
- style debitor stroke-width:6px
-
- user:hsOfficeDebitor.creator([debitor.creator])
- %% created by role
- user:hsOfficeDebitor.creator --> role:hsOfficePartner.agent
-
- role:hsOfficeDebitor.owner[debitor.owner]
- %% permissions
- role:hsOfficeDebitor.owner --> perm:hsOfficeDebitor.*{{debitor.*}}
- %% incoming
- user:hsOfficeDebitor.creator --> role:hsOfficeDebitor.owner
- role:global.admin --> role:hsOfficeDebitor.owner
-
- role:hsOfficeDebitor.admin[debitor.admin]
- %% permissions
- role:hsOfficeDebitor.admin --> perm:hsOfficeDebitor.edit{{debitor.edit}}
- %% incoming
- role:hsOfficeDebitor.owner ---> role:hsOfficeDebitor.admin
-
- role:hsOfficeDebitor.agent[debitor.agent]
- %% incoming
- role:hsOfficeDebitor.admin ---> role:hsOfficeDebitor.agent
- role:hsOfficePartner.admin --> role:hsOfficeDebitor.agent
- %% outgoing
- role:hsOfficeDebitor.agent --> role:hsOfficeBankAccount.tenant
-
- role:hsOfficeDebitor.tenant[debitor.tenant]
- %% incoming
- role:hsOfficeDebitor.agent ---> role:hsOfficeDebitor.tenant
- role:hsOfficePartner.agent --> role:hsOfficeDebitor.tenant
- role:hsOfficeBankAccount.admin --> role:hsOfficeDebitor.tenant
- %% outgoing
- role:hsOfficeDebitor.tenant --> role:hsOfficePartner.tenant
- role:hsOfficeDebitor.tenant --> role:hsOfficeContact.guest
-
- role:hsOfficeDebitor.guest[debitor.guest]
- %% permissions
- role:hsOfficeDebitor.guest --> perm:hsOfficeDebitor.view{{debitor.view}}
- %% incoming
- role:hsOfficeDebitor.tenant --> role:hsOfficeDebitor.guest
- end
-
-end
+ subgraph debitorRel.anchorPerson:roles[ ]
+ style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
-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
-
- subgraph package
- style package fill: #e9f7ef
-
- role:package.owner[package.owner]
- --> role:package.admin[package.admin]
- --> role:package.tenant[package.tenant]
-
- role:hsOfficeDebitor.agent --> role:package.owner
- role:package.admin --> role:hsOfficeDebitor.tenant
- role:hsOfficePartner.tenant --> role:hsOfficeDebitor.guest
+ 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.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 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: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.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 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 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: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
```
-
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
index 5f684f49..065efff6 100644
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
@@ -15,233 +17,211 @@ call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
-- ============================================================================
---changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-debitor-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for debitor entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeDebitorRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeDebitor(
+ NEW hs_office_debitor
+)
+ language plpgsql as $$
+
declare
- hsOfficeDebitorTenant RbacRoleDescriptor;
- oldPartner hs_office_partner;
- newPartner hs_office_partner;
- newPerson hs_office_person;
- oldContact hs_office_contact;
- newContact hs_office_contact;
- newBankAccount hs_office_bankaccount;
- oldBankAccount hs_office_bankaccount;
+ newPartnerRel hs_office_relation;
+ newDebitorRel hs_office_relation;
+ newRefundBankAccount hs_office_bankaccount;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- hsOfficeDebitorTenant := hsOfficeDebitorTenant(NEW);
+ 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_partner as p where p.uuid = NEW.partnerUuid into newPartner;
- select * from hs_office_person as p where p.uuid = newPartner.personUuid into newPerson;
- select * from hs_office_contact as c where c.uuid = NEW.billingContactUuid into newContact;
- select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount;
- if TG_OP = 'INSERT' then
+ 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;
- perform createRoleWithGrants(
- hsOfficeDebitorOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
- userUuids => array[currentUserUuid()],
- grantedByRole => globalAdmin()
- );
+ 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));
- perform createRoleWithGrants(
- hsOfficeDebitorAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeDebitorAgent(NEW),
- incomingSuperRoles => array[
- hsOfficeDebitorAdmin(NEW),
- hsOfficePartnerAdmin(newPartner),
- hsOfficeContactAdmin(newContact)],
- outgoingSubRoles => array[
- hsOfficeBankAccountTenant(newBankaccount)]
- );
-
- perform createRoleWithGrants(
- hsOfficeDebitorTenant(NEW),
- incomingSuperRoles => array[
- hsOfficeDebitorAgent(NEW),
- hsOfficePartnerAgent(newPartner),
- hsOfficeBankAccountAdmin(newBankaccount)],
- outgoingSubRoles => array[
- hsOfficePartnerTenant(newPartner),
- hsOfficeContactGuest(newContact),
- hsOfficeBankAccountGuest(newBankaccount)]
- );
-
- perform createRoleWithGrants(
- hsOfficeDebitorGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[
- hsOfficeDebitorTenant(NEW)]
- );
-
- elsif TG_OP = 'UPDATE' then
-
- if OLD.partnerUuid <> NEW.partnerUuid then
- select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner;
-
- call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficePartnerAdmin(oldPartner));
- call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficePartnerAdmin(newPartner));
-
- call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficePartnerAgent(oldPartner));
- call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficePartnerAgent(newPartner));
-
- call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorTenant(OLD));
- call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorTenant(NEW));
- end if;
-
- if OLD.billingContactUuid <> NEW.billingContactUuid then
- select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact;
-
- call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeContactAdmin(oldContact));
- call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeContactAdmin(newContact));
-
- call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficeDebitorTenant(OLD));
- call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficeDebitorTenant(NEW));
- end if;
-
- if (OLD.refundBankAccountUuid is not null or NEW.refundBankAccountUuid is not null) and
- ( OLD.refundBankAccountUuid is null or NEW.refundBankAccountUuid is null or
- OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid ) then
-
- select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount;
-
- if oldBankAccount is not null then
- call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankaccount), hsOfficeDebitorAgent(OLD));
- end if;
- if newBankAccount is not null then
- call grantRoleToRole(hsOfficeBankAccountTenant(newBankaccount), hsOfficeDebitorAgent(NEW));
- end if;
-
- if oldBankAccount is not null then
- call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeBankAccountAdmin(oldBankaccount));
- end if;
- if newBankAccount is not null then
- call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeBankAccountAdmin(newBankaccount));
- end if;
-
- if oldBankAccount is not null then
- call revokeRoleFromRole(hsOfficeBankAccountGuest(oldBankaccount), hsOfficeDebitorTenant(OLD));
- end if;
- if newBankAccount is not null then
- call grantRoleToRole(hsOfficeBankAccountGuest(newBankaccount), hsOfficeDebitorTenant(NEW));
- end if;
- end if;
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ 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);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new debitor.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_debitor row.
*/
-create trigger createRbacRolesForHsOfficeDebitor_Trigger
- after insert
- on hs_office_debitor
- for each row
-execute procedure hsOfficeDebitorRbacRolesTrigger();
-/*
- An AFTER UPDATE TRIGGER which updates the role structure of a debitor.
- */
-create trigger updateRbacRolesForHsOfficeDebitor_Trigger
- after update
- on hs_office_debitor
+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 hsOfficeDebitorRbacRolesTrigger();
+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 generateRbacIdentityViewFromProjection('hs_office_debitor', $idName$
- '#' ||
- (select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
- to_char(debitorNumberSuffix, 'fm00') ||
- ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid)
- $idName$);
---//
+ 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', 'target.debitorNumberSuffix',
+call generateRbacRestrictedView('hs_office_debitor',
+ $orderBy$
+ defaultPrefix
+ $orderBy$,
$updates$
- partnerUuid = new.partnerUuid, -- TODO: remove? should never do anything
+ debitorRelUuid = new.debitorRelUuid,
billable = new.billable,
- billingContactUuid = new.billingContactUuid,
- debitorNumberSuffix = new.debitorNumberSuffix, -- TODO: Should it be allowed to updated this value?
refundBankAccountUuid = new.refundBankAccountUuid,
vatId = new.vatId,
vatCountryCode = new.vatCountryCode,
vatBusiness = new.vatBusiness,
- vatreversecharge = new.vatreversecharge,
- defaultPrefix = new.defaultPrefix -- TODO: Should it be allowed to updated this value?
+ vatReverseCharge = new.vatReverseCharge,
+ defaultPrefix = new.defaultPrefix
$updates$);
--//
--- ============================================================================
---changeset hs-office-debitor-rbac-NEW-DEBITOR:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-debitor and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addDebitorPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-debitor permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addDebitorPermissions := createPermissions(globalObjectUuid, array ['new-debitor']);
- call grantPermissionsToRole(globalAdminRoleUuid, addDebitorPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-debitor to current user respectively assumed roles.
- */
-create or replace function addHsOfficeDebitorNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-debitor not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
-end; $$;
-
-/**
- Checks if the user or assumed roles are allowed to create a new debitor.
- */
-create trigger hs_office_debitor_insert_trigger
- before insert
- on hs_office_debitor
- for each row
- -- TODO.spec: who is allowed to create new debitors
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeDebitorNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
index af75d074..5a485b31 100644
--- a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
+++ b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
@@ -9,36 +9,41 @@
Creates a single debitor test record.
*/
create or replace procedure createHsOfficeDebitorTestData(
- debitorNumberSuffix numeric(5),
- partnerTradeName varchar,
- billingContactLabel varchar,
- defaultPrefix varchar
+ withDebitorNumberSuffix numeric(5),
+ forPartnerPersonName varchar,
+ forBillingContactLabel varchar,
+ withDefaultPrefix varchar
)
language plpgsql as $$
declare
currentTask varchar;
idName varchar;
- relatedPartner hs_office_partner;
- relatedContact hs_office_contact;
+ relatedDebitorRelUuid uuid;
relatedBankAccountUuid uuid;
begin
- idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel);
+ 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 partner.* from hs_office_partner partner
- join hs_office_person person on person.uuid = partner.personUuid
- where person.tradeName = partnerTradeName into relatedPartner;
- select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact;
- select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid;
+ 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';
- raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
- raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
- raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;
+ 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, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
- values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, false, relatedBankAccountUuid, defaultPrefix);
+ into hs_office_debitor (uuid, debitorRelUuid, debitornumbersuffix, billable, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
+ values (uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true, true, false, relatedBankAccountUuid, withDefaultPrefix);
end; $$;
--//
diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql
index acc0651a..f2a560e2 100644
--- a/src/main/resources/db/changelog/300-hs-office-membership.sql
+++ b/src/main/resources/db/changelog/300-hs-office-membership.sql
@@ -12,7 +12,6 @@ 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),
- mainDebitorUuid uuid not null references hs_office_debitor(uuid),
memberNumberSuffix char(2) not null check (
memberNumberSuffix::text ~ '^[0-9][0-9]$'),
validity daterange not null,
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
index 8cf604ab..4f425f6e 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
@@ -1,75 +1,159 @@
-### hs_office_membership RBAC
+### rbac membership
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph global
- style global fill:#eee
-
- role:global.admin[global.admin]
-end
-
-subgraph hsOfficeDebitor
+subgraph partnerRel["`**partnerRel**`"]
direction TB
- style hsOfficeDebitor fill:#eee
-
- role:hsOfficeDebitor.owner[debitor.owner]
- --> role:hsOfficeDebitor.admin[debitor.admin]
- --> role:hsOfficeDebitor.tenant[debitor.tenant]
- --> role:hsOfficeDebitor.guest[debitor.guest]
+ style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+
+ 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 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
+
+ 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 hsOfficePartner
+subgraph partnerRel.contact["`**partnerRel.contact**`"]
direction TB
- style hsOfficePartner fill:#eee
-
- role:hsOfficePartner.owner[partner.admin]
- --> role:hsOfficePartner.admin[partner.admin]
- --> role:hsOfficePartner.agent[partner.agent]
- --> role:hsOfficePartner.tenant[partner.tenant]
- --> role:hsOfficePartner.guest[partner.guest]
+ 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 hsOfficeMembership
-
- role:hsOfficeMembership.owner[membership.owner]
- %% permissions
- role:hsOfficeMembership.owner --> perm:hsOfficeMembership.*{{membership.*}}
- %% incoming
- role:global.admin ---> role:hsOfficeMembership.owner
-
- role:hsOfficeMembership.admin[membership.admin]
- %% permissions
- role:hsOfficeMembership.admin --> perm:hsOfficeMembership.edit{{membership.edit}}
- %% incoming
- role:hsOfficeMembership.owner ---> role:hsOfficeMembership.admin
-
- role:hsOfficeMembership.agent[membership.agent]
- %% incoming
- role:hsOfficeMembership.admin ---> role:hsOfficeMembership.agent
- role:hsOfficePartner.admin --> role:hsOfficeMembership.agent
- role:hsOfficeDebitor.admin --> role:hsOfficeMembership.agent
- %% outgoing
- role:hsOfficeMembership.agent --> role:hsOfficePartner.tenant
- role:hsOfficeMembership.agent --> role:hsOfficeDebitor.tenant
-
- role:hsOfficeMembership.tenant[membership.tenant]
- %% incoming
- role:hsOfficeMembership.agent --> role:hsOfficeMembership.tenant
- role:hsOfficePartner.agent --> role:hsOfficeMembership.tenant
- role:hsOfficeDebitor.agent --> role:hsOfficeMembership.tenant
- %% outgoing
- role:hsOfficeMembership.tenant --> role:hsOfficePartner.guest
- role:hsOfficeMembership.tenant --> role:hsOfficeDebitor.guest
+subgraph membership["`**membership**`"]
+ direction TB
+ style membership fill:#dd4901,stroke:#274d6e,stroke-width:8px
- role:hsOfficeMembership.guest[membership.guest]
- %% permissions
- role:hsOfficeMembership.guest --> perm:hsOfficeMembership.view{{membership.view}}
- %% incoming
- role:hsOfficeMembership.tenant --> role:hsOfficeMembership.guest
- role:hsOfficePartner.tenant --> role:hsOfficeMembership.guest
- role:hsOfficeDebitor.tenant --> role:hsOfficeMembership.guest
+ subgraph membership:roles[ ]
+ style membership:roles fill:#dd4901,stroke:white
+
+ role:membership:owner[[membership:owner]]
+ role:membership:admin[[membership:admin]]
+ role:membership:referrer[[membership:referrer]]
+ 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:partnerRel:admin ==> role:membership:owner
+role:membership:owner ==> role:membership:admin
+role:partnerRel:agent ==> role:membership:admin
+role:membership:admin ==> role:membership:referrer
+role:membership:referrer ==> role:partnerRel:tenant
+
+%% granting permissions to roles
+role:global:admin ==> perm:membership:INSERT
+role:membership:owner ==> perm:membership:DELETE
+role:membership:admin ==> perm:membership:UPDATE
+role:membership:referrer ==> perm:membership:SELECT
```
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
index 2a4a4a50..17dbc84c 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
@@ -1,4 +1,6 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
--changeset hs-office-membership-rbac-OBJECT:1 endDelimiter:--//
@@ -15,148 +17,162 @@ call generateRbacRoleDescriptors('hsOfficeMembership', 'hs_office_membership');
-- ============================================================================
---changeset hs-office-membership-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-membership-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the roles and their assignments for membership entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeMembershipRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeMembership(
+ NEW hs_office_membership
+)
+ language plpgsql as $$
+
declare
- newHsOfficePartner hs_office_partner;
- newHsOfficeDebitor hs_office_debitor;
+ newPartnerRel hs_office_relation;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newHsOfficePartner;
- select * from hs_office_debitor as c where c.uuid = NEW.mainDebitorUuid into newHsOfficeDebitor;
+ 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);
- if TG_OP = 'INSERT' then
- -- === ATTENTION: code generated from related Mermaid flowchart: ===
+ perform createRoleWithGrants(
+ hsOfficeMembershipOwner(NEW),
+ permissions => array['DELETE'],
+ incomingSuperRoles => array[hsOfficeRelationAdmin(newPartnerRel)],
+ userUuids => array[currentUserUuid()]
+ );
- perform createRoleWithGrants(
- hsOfficeMembershipOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()]
- );
+ perform createRoleWithGrants(
+ hsOfficeMembershipAdmin(NEW),
+ permissions => array['UPDATE'],
+ incomingSuperRoles => array[
+ hsOfficeMembershipOwner(NEW),
+ hsOfficeRelationAgent(newPartnerRel)]
+ );
- perform createRoleWithGrants(
- hsOfficeMembershipAdmin(NEW),
- permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
- );
-
- perform createRoleWithGrants(
- hsOfficeMembershipAgent(NEW),
- incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW), hsOfficePartnerAdmin(newHsOfficePartner), hsOfficeDebitorAdmin(newHsOfficeDebitor)],
- outgoingSubRoles => array[hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
- );
-
- perform createRoleWithGrants(
- hsOfficeMembershipTenant(NEW),
- incomingSuperRoles => array[hsOfficeMembershipAgent(NEW), hsOfficePartnerAgent(newHsOfficePartner), hsOfficeDebitorAgent(newHsOfficeDebitor)],
- outgoingSubRoles => array[hsOfficePartnerGuest(newHsOfficePartner), hsOfficeDebitorGuest(newHsOfficeDebitor)]
- );
-
- perform createRoleWithGrants(
- hsOfficeMembershipGuest(NEW),
- permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
- );
-
- -- === END of code generated from Mermaid flowchart. ===
-
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ perform createRoleWithGrants(
+ hsOfficeMembershipReferrer(NEW),
+ permissions => array['SELECT'],
+ incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW)],
+ outgoingSubRoles => array[hsOfficeRelationTenant(newPartnerRel)]
+ );
call leaveTriggerForObjectUuid(NEW.uuid);
- return NEW;
end; $$;
/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
+ AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_membership row.
*/
-create trigger createRbacRolesForHsOfficeMembership_Trigger
- after insert
- on hs_office_membership
+
+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 hsOfficeMembershipRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficeMembership_tf();
--//
-- ============================================================================
---changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-membership-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_membership', $idName$
- '#' ||
- (select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
- memberNumberSuffix ||
- ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid)
- $idName$);
+
+/*
+ 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 => 'target.memberNumberSuffix',
- columnUpdates => $updates$
+ $orderBy$
+ validity
+ $orderBy$,
+ $updates$
validity = new.validity,
- reasonForTermination = new.reasonForTermination,
- membershipFeeBillable = new.membershipFeeBillable
+ membershipFeeBillable = new.membershipFeeBillable,
+ reasonForTermination = new.reasonForTermination
$updates$);
--//
-
--- ============================================================================
---changeset hs-office-membership-rbac-NEW-Membership:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-/*
- Creates a global permission for new-membership and assigns it to the hostsharing admins role.
- */
-do language plpgsql $$
- declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
- begin
- call defineContext('granting global new-membership permission to global admin role', null, null, null);
-
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-membership']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
-$$;
-
-/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeMembershipNotAllowedForCurrentSubjects()
- returns trigger
- language PLPGSQL
-as $$
-begin
- raise exception '[403] new-membership 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_membership_insert_trigger
- before insert
- on hs_office_membership
- for each row
- -- TODO.spec: who is allowed to create new memberships
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeMembershipNotAllowedForCurrentSubjects();
---//
-
diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
index 637c87ca..9d574a58 100644
--- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
+++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
@@ -9,35 +9,27 @@
Creates a single membership test record.
*/
create or replace procedure createHsOfficeMembershipTestData(
- forPartnerTradeName varchar,
- forMainDebitorNumberSuffix numeric,
+ forPartnerNumber numeric(5),
newMemberNumberSuffix char(2) )
language plpgsql as $$
declare
currentTask varchar;
- idName varchar;
relatedPartner hs_office_partner;
- relatedDebitor hs_office_debitor;
begin
- idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumberSuffix);
- currentTask := 'creating Membership test-data ' || idName;
+ 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
- join hs_office_person person on person.uuid = partner.personUuid
- where person.tradeName = forPartnerTradeName into relatedPartner;
- select d.* from hs_office_debitor d
- where d.partneruuid = relatedPartner.uuid
- and d.debitorNumberSuffix = forMainDebitorNumberSuffix
- into relatedDebitor;
+ where partner.partnerNumber = forPartnerNumber into relatedPartner;
- raise notice 'creating test Membership: %', idName;
+ raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix;
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
- raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert
- into hs_office_membership (uuid, partneruuid, maindebitoruuid, memberNumberSuffix, validity, reasonfortermination)
- values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE');
+ into hs_office_membership (uuid, partneruuid, memberNumberSuffix, validity, reasonfortermination)
+ values (uuid_generate_v4(), relatedPartner.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE');
end; $$;
--//
@@ -48,9 +40,9 @@ end; $$;
do language plpgsql $$
begin
- call createHsOfficeMembershipTestData('First GmbH', 11, '01');
- call createHsOfficeMembershipTestData('Second e.K.', 12, '02');
- call createHsOfficeMembershipTestData('Third OHG', 13, '03');
+ call createHsOfficeMembershipTestData(10001, '01');
+ call createHsOfficeMembershipTestData(10002, '02');
+ call createHsOfficeMembershipTestData(10003, '03');
end;
$$;
--//
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
index 5ee8bfbe..a4cac136 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
@@ -42,7 +42,7 @@ begin
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
+ getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
createPermissions(NEW.uuid, array ['SELECT'])
);
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
index 69920385..035da07b 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
@@ -42,7 +42,7 @@ begin
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
+ getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
createPermissions(NEW.uuid, array ['SELECT'])
);
diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
index fa49e102..be612e90 100644
--- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
@@ -129,7 +129,8 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeBankAccountPackageRule = classes()
.that().resideInAPackage("..hs.office.bankaccount..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.bankaccount..",
+ .resideInAnyPackage(
+ "..hs.office.bankaccount..",
"..hs.office.sepamandate..",
"..hs.office.debitor..",
"..hs.office.migration..");
@@ -139,7 +140,8 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeSepaMandatePackageRule = classes()
.that().resideInAPackage("..hs.office.sepamandate..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.sepamandate..",
+ .resideInAnyPackage(
+ "..hs.office.sepamandate..",
"..hs.office.debitor..",
"..hs.office.migration..");
@@ -148,7 +150,9 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeContactPackageRule = classes()
.that().resideInAPackage("..hs.office.contact..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.contact..", "..hs.office.relation..",
+ .resideInAnyPackage(
+ "..hs.office.contact..",
+ "..hs.office.relation..",
"..hs.office.partner..",
"..hs.office.debitor..",
"..hs.office.membership..",
@@ -159,37 +163,46 @@ public class ArchitectureTest {
public static final ArchRule hsOfficePersonPackageRule = classes()
.that().resideInAPackage("..hs.office.person..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.person..", "..hs.office.relation..",
+ .resideInAnyPackage(
+ "..hs.office.person..",
+ "..hs.office.relation..",
"..hs.office.partner..",
"..hs.office.debitor..",
"..hs.office.membership..",
- "..hs.office.migration..");
+ "..hs.office.migration..")
+ .orShould().haveNameNotMatching(".*Test$");
+
@ArchTest
@SuppressWarnings("unused")
public static final ArchRule hsOfficeRelationPackageRule = classes()
.that().resideInAPackage("..hs.office.relation..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.relation..",
+ .resideInAnyPackage(
+ "..hs.office.relation..",
"..hs.office.partner..",
- "..hs.office.migration..");
+ "..hs.office.migration..")
+ .orShould().haveNameNotMatching(".*Test$");
@ArchTest
@SuppressWarnings("unused")
public static final ArchRule hsOfficePartnerPackageRule = classes()
.that().resideInAPackage("..hs.office.partner..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.partner..",
+ .resideInAnyPackage(
+ "..hs.office.partner..",
"..hs.office.debitor..",
"..hs.office.membership..",
- "..hs.office.migration..");
+ "..hs.office.migration..")
+ .orShould().haveNameNotMatching(".*Test$");
@ArchTest
@SuppressWarnings("unused")
public static final ArchRule hsOfficeMembershipPackageRule = classes()
.that().resideInAPackage("..hs.office.membership..")
.should().onlyBeAccessed().byClassesThat()
- .resideInAnyPackage("..hs.office.membership..",
+ .resideInAnyPackage(
+ "..hs.office.membership..",
"..hs.office.coopassets..",
"..hs.office.coopshares..",
"..hs.office.migration..");
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntityUnitTest.java
index 9fea3b5e..acd6c8f3 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntityUnitTest.java
@@ -19,7 +19,7 @@ class HsOfficeBankAccountEntityUnitTest {
.iban("DE02370502990000684712")
.bic("COKSDE33")
.build();
- assertThat("" + givenBankAccount).isEqualTo("bankAccount(holder='given holder', iban='DE02370502990000684712', bic='COKSDE33')");
+ assertThat(givenBankAccount.toString()).isEqualTo("bankAccount(DE02370502990000684712: holder='given holder', bic='COKSDE33')");
}
@Test
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
index eb14e634..fd484c4c 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
@@ -102,23 +102,21 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
final var roles = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(roles)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_bankaccount#sometempaccC.owner",
- "hs_office_bankaccount#sometempaccC.admin",
- "hs_office_bankaccount#sometempaccC.tenant",
- "hs_office_bankaccount#sometempaccC.guest"
+ "hs_office_bankaccount#DE25500105176934832579.owner",
+ "hs_office_bankaccount#DE25500105176934832579.admin",
+ "hs_office_bankaccount#DE25500105176934832579.referrer"
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm DELETE on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
- "{ grant role hs_office_bankaccount#sometempaccC.owner to role global#global.admin by system and assume }",
- "{ grant role hs_office_bankaccount#sometempaccC.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }",
+ "{ grant perm DELETE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
+ "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to role global#global.admin by system and assume }",
+ "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to user selfregistered-user-drew@hostsharing.org by hs_office_bankaccount#DE25500105176934832579.owner and assume }",
- "{ grant role hs_office_bankaccount#sometempaccC.admin to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
+ "{ grant role hs_office_bankaccount#DE25500105176934832579.admin to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
+ "{ grant perm UPDATE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
- "{ grant role hs_office_bankaccount#sometempaccC.tenant to role hs_office_bankaccount#sometempaccC.admin by system and assume }",
-
- "{ grant perm SELECT on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.guest by system and assume }",
- "{ grant role hs_office_bankaccount#sometempaccC.guest to role hs_office_bankaccount#sometempaccC.tenant by system and assume }",
+ "{ grant perm SELECT on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.referrer by system and assume }",
+ "{ grant role hs_office_bankaccount#DE25500105176934832579.referrer to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
null
));
}
@@ -241,10 +239,6 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
final var givenBankAccount = givenSomeTemporaryBankAccount("selfregistered-user-drew@hostsharing.org");
- assertThat(distinctRoleNamesOf(rawRoleRepo.findAll()).size()).as("unexpected number of roles created")
- .isEqualTo(initialRoleNames.size() + 4);
- assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()).size()).as("unexpected number of grants created")
- .isEqualTo(initialGrantNames.size() + 7);
// when
final var result = jpaAttempt.transacted(() -> {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
index 91ee8bde..259f88fe 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
@@ -105,19 +105,18 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean
initialRoleNames,
"hs_office_contact#anothernewcontact.owner",
"hs_office_contact#anothernewcontact.admin",
- "hs_office_contact#anothernewcontact.tenant",
- "hs_office_contact#anothernewcontact.guest"
+ "hs_office_contact#anothernewcontact.referrer"
));
- assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from(
+ assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
- "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.tenant to role hs_office_contact#anothernewcontact.admin by system and assume }",
- "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
- "{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.guest by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.guest to role hs_office_contact#anothernewcontact.tenant by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
+ "{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
+ "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
+ "{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by hs_office_contact#anothernewcontact.owner and assume }",
+ "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
+ "{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
+
+ "{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.referrer by system and assume }",
+ "{ grant role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }"
));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java
index 04122059..2c9a811d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java
@@ -276,7 +276,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
@Test
@Accepts({ "CoopAssetTransaction:X(Access Control)" })
- void contactAdminUser_canGetRelatedCoopAssetTransaction() {
+ void partnerPersonUser_canGetRelatedCoopAssetTransaction() {
context.define("superuser-alex@hostsharing.net");
final var givenCoopAssetTransactionUuid = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
null,
@@ -285,7 +285,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest extends ContextBased
RestAssured // @formatter:off
.given()
- .header("current-user", "contact-admin@firstcontact.example.com")
+ .header("current-user", "person-FirstGmbH@example.com")
.port(port)
.when()
.get("http://localhost/api/hs/office/coopassetstransactions/" + givenCoopAssetTransactionUuid)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java
index d93aa90f..82ba35e3 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java
@@ -23,27 +23,27 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest {
void toStringContainsAlmostAllPropertiesAccount() {
final var result = givenCoopAssetTransaction.toString();
- assertThat(result).isEqualTo("CoopAssetsTransaction(1000101, 2020-01-01, DEPOSIT, 128.00, some-ref)");
+ assertThat(result).isEqualTo("CoopAssetsTransaction(M-1000101: 2020-01-01, DEPOSIT, 128.00, some-ref)");
}
@Test
void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() {
final var result = givenCoopAssetTransaction.toShortString();
- assertThat(result).isEqualTo("1000101+128.00");
+ assertThat(result).isEqualTo("M-1000101:+128.00");
}
@Test
void toStringWithEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopAssetsTransaction.toString();
- assertThat(result).isEqualTo("CoopAssetsTransaction()");
+ assertThat(result).isEqualTo("CoopAssetsTransaction(M-?????: )");
}
@Test
void toShortStringEmptyTransactionDoesNotThrowException() {
final var result = givenEmptyCoopAssetsTransaction.toShortString();
- assertThat(result).isEqualTo("nullnu");
+ assertThat(result).isEqualTo("M-?????:+0.00");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
index 1f6964b8..90ab1f00 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
@@ -114,7 +114,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
+ "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
null));
}
@@ -141,17 +141,17 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopAssetsTransactionsAreReturned(
result,
- "CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
- "CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
- "CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)",
+ "CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
+ "CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
+ "CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)",
- "CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
- "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
- "CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)",
+ "CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
+ "CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
+ "CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)",
- "CoopAssetsTransaction(1000303, 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
- "CoopAssetsTransaction(1000303, 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
- "CoopAssetsTransaction(1000303, 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)");
+ "CoopAssetsTransaction(M-1000303: 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)",
+ "CoopAssetsTransaction(M-1000303: 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)",
+ "CoopAssetsTransaction(M-1000303: 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)");
}
@Test
@@ -169,9 +169,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopAssetsTransactionsAreReturned(
result,
- "CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
- "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
- "CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)");
+ "CoopAssetsTransaction(M-1000202: 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)",
+ "CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)",
+ "CoopAssetsTransaction(M-1000202: 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)");
}
@Test
@@ -189,13 +189,13 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// then
allTheseCoopAssetsTransactionsAreReturned(
result,
- "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)");
+ "CoopAssetsTransaction(M-1000202: 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)");
}
@Test
- public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() {
+ public void partnerPersonAdmin_canViewRelatedCoopAssetsTransactions() {
// given:
- context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_person#FirstGmbH.admin");
// when:
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
@@ -206,9 +206,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
// then:
exactlyTheseCoopAssetsTransactionsAreReturned(
result,
- "CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
- "CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
- "CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)");
+ "CoopAssetsTransaction(M-1000101: 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)",
+ "CoopAssetsTransaction(M-1000101: 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)",
+ "CoopAssetsTransaction(M-1000101: 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java
index 3d120cd1..d6291512 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java
@@ -218,17 +218,27 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest extends ContextBased
@Test
@Accepts({"CoopShareTransaction:X(Access Control)"})
- void contactAdminUser_canGetRelatedCoopShareTransaction() {
+ void partnerPersonUser_canGetRelatedCoopShareTransaction() {
context.define("superuser-alex@hostsharing.net");
final var givenCoopShareTransactionUuid = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(null, LocalDate.of(2010, 3, 15), LocalDate.of(2010, 3, 15)).get(0).getUuid();
RestAssured // @formatter:off
- .given().header("current-user", "contact-admin@firstcontact.example.com").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions/" + givenCoopShareTransactionUuid).then().log().body().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals("""
- {
- "transactionType": "SUBSCRIPTION",
- "shareCount": 4
- }
- """)); // @formatter:on
+ .given()
+ .header("current-user", "person-FirstGmbH@example.com")
+ .port(port)
+ .when()
+ .get("http://localhost/api/hs/office/coopsharestransactions/" + givenCoopShareTransactionUuid)
+ .then()
+ .log().body()
+ .assertThat()
+ .statusCode(200)
+ .contentType("application/json")
+ .body("", lenientlyEquals("""
+ {
+ "transactionType": "SUBSCRIPTION",
+ "shareCount": 4
+ }
+ """)); // @formatter:on
}
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
index 609e7940..837e02fd 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
@@ -88,7 +88,6 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
- .map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.toList();
@@ -109,11 +108,10 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(initialRoleNames)); // no new roles created
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
- .map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
+ "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
null));
}
@@ -194,7 +192,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given:
- context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000101.admin");
// when:
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
index 0616e338..975ad961 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
@@ -7,6 +7,9 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.test.Accepts;
import net.hostsharing.test.JpaAttempt;
@@ -24,6 +27,7 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.UUID;
+import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
@@ -57,6 +61,12 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
@Autowired
HsOfficeBankAccountRepository bankAccountRepo;
+ @Autowired
+ HsOfficePersonRepository personRepo;
+
+ @Autowired
+ HsOfficeRelationRepository relRepo;
+
@Autowired
JpaAttempt jpaAttempt;
@@ -81,37 +91,135 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType("application/json")
.body("", lenientlyEquals("""
[
- {
- "debitorNumber": 1000111,
- "debitorNumberSuffix": 11,
- "partner": { "person": { "personType": "LEGAL_PERSON" } },
- "billingContact": { "label": "first contact" },
- "vatId": null,
- "vatCountryCode": null,
- "vatBusiness": true,
- "refundBankAccount": { "holder": "First GmbH" }
- },
- {
- "debitorNumber": 1000212,
- "debitorNumberSuffix": 12,
- "partner": { "person": { "tradeName": "Second e.K." } },
- "billingContact": { "label": "second contact" },
- "vatId": null,
- "vatCountryCode": null,
- "vatBusiness": true,
- "refundBankAccount": { "holder": "Second e.K." }
- },
- {
- "debitorNumber": 1000313,
- "debitorNumberSuffix": 13,
- "partner": { "person": { "tradeName": "Third OHG" } },
- "billingContact": { "label": "third contact" },
- "vatId": null,
- "vatCountryCode": null,
- "vatBusiness": true,
- "refundBankAccount": { "holder": "Third OHG" }
- }
- ]
+ {
+ "debitorRel": {
+ "anchor": {
+ "personType": "LEGAL_PERSON",
+ "tradeName": "First GmbH",
+ "givenName": null,
+ "familyName": null
+ },
+ "holder": {
+ "personType": "LEGAL_PERSON",
+ "tradeName": "First GmbH",
+ "givenName": null,
+ "familyName": null
+ },
+ "type": "DEBITOR",
+ "mark": null,
+ "contact": {
+ "label": "first contact",
+ "emailAddresses": "contact-admin@firstcontact.example.com",
+ "phoneNumbers": "+49 123 1234567"
+ }
+ },
+ "debitorNumber": 1000111,
+ "debitorNumberSuffix": 11,
+ "partner": {
+ "partnerNumber": 10001,
+ "partnerRel": {
+ "anchor": {
+ "personType": "LEGAL_PERSON",
+ "tradeName": "Hostsharing eG",
+ "givenName": null,
+ "familyName": null
+ },
+ "holder": {
+ "personType": "LEGAL_PERSON",
+ "tradeName": "First GmbH",
+ "givenName": null,
+ "familyName": null
+ },
+ "type": "PARTNER",
+ "mark": null,
+ "contact": {
+ "label": "first contact",
+ "emailAddresses": "contact-admin@firstcontact.example.com",
+ "phoneNumbers": "+49 123 1234567"
+ }
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789",
+ "birthName": null,
+ "birthPlace": null,
+ "birthday": null,
+ "dateOfDeath": null
+ }
+ },
+ "billable": true,
+ "vatId": null,
+ "vatCountryCode": null,
+ "vatBusiness": true,
+ "vatReverseCharge": false,
+ "refundBankAccount": {
+ "holder": "First GmbH",
+ "iban": "DE02120300000000202051",
+ "bic": "BYLADEM1001"
+ },
+ "defaultPrefix": "fir"
+ },
+ {
+ "debitorRel": {
+ "anchor": {"tradeName": "Second e.K."},
+ "holder": {"tradeName": "Second e.K."},
+ "type": "DEBITOR",
+ "contact": {"emailAddresses": "contact-admin@secondcontact.example.com"}
+ },
+ "debitorNumber": 1000212,
+ "debitorNumberSuffix": 12,
+ "partner": {
+ "partnerNumber": 10002,
+ "partnerRel": {
+ "anchor": {"tradeName": "Hostsharing eG"},
+ "holder": {"tradeName": "Second e.K."},
+ "type": "PARTNER",
+ "contact": {"emailAddresses": "contact-admin@secondcontact.example.com"}
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789"
+ }
+ },
+ "billable": true,
+ "vatId": null,
+ "vatCountryCode": null,
+ "vatBusiness": true,
+ "vatReverseCharge": false,
+ "refundBankAccount": {"iban": "DE02100500000054540402"},
+ "defaultPrefix": "sec"
+ },
+ {
+ "debitorRel": {
+ "anchor": {"tradeName": "Third OHG"},
+ "holder": {"tradeName": "Third OHG"},
+ "type": "DEBITOR",
+ "contact": {"emailAddresses": "contact-admin@thirdcontact.example.com"}
+ },
+ "debitorNumber": 1000313,
+ "debitorNumberSuffix": 13,
+ "partner": {
+ "partnerNumber": 10003,
+ "partnerRel": {
+ "anchor": {"tradeName": "Hostsharing eG"},
+ "holder": {"tradeName": "Third OHG"},
+ "type": "PARTNER",
+ "contact": {"emailAddresses": "contact-admin@thirdcontact.example.com"}
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789"
+ }
+ },
+ "billable": true,
+ "vatId": null,
+ "vatCountryCode": null,
+ "vatBusiness": true,
+ "vatReverseCharge": false,
+ "refundBankAccount": {"iban": "DE02300209000106531065"},
+ "defaultPrefix": "thi"
+ }
+ ]
"""));
// @formatter:on
}
@@ -132,8 +240,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
[
{
"debitorNumber": 1000212,
- "partner": { "person": { "tradeName": "Second e.K." } },
- "billingContact": { "label": "second contact" },
+ "partner": { "partnerNumber": 10002 },
+ "debitorRel": {
+ "contact": { "label": "second contact" }
+ },
"vatId": null,
"vatCountryCode": null,
"vatBusiness": true
@@ -154,6 +264,17 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike("Fourth").get(0);
+ final var givenBillingPerson = personRepo.findPersonByOptionalNameLike("Fourth").get(0);
+
+ final var givenDebitorRelUUid = jpaAttempt.transacted(() -> {
+ context.define("superuser-alex@hostsharing.net");
+ return relRepo.save(HsOfficeRelationEntity.builder()
+ .type(DEBITOR)
+ .anchor(givenPartner.getPartnerRel().getHolder())
+ .holder(givenBillingPerson)
+ .contact(givenContact)
+ .build()).getUuid();
+ }).assertSuccessful().returnedValue();
final var location = RestAssured // @formatter:off
.given()
@@ -161,8 +282,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType(ContentType.JSON)
.body("""
{
- "partnerUuid": "%s",
- "billingContactUuid": "%s",
+ "debitorRelUuid": "%s",
"debitorNumberSuffix": "%s",
"billable": "true",
"vatId": "VAT123456",
@@ -172,7 +292,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
"refundBankAccountUuid": "%s",
"defaultPrefix": "for"
}
- """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix, givenBankAccount.getUuid()))
+ """.formatted( givenDebitorRelUUid, ++nextDebitorSuffix, givenBankAccount.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@@ -182,8 +302,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("uuid", isUuidValid())
.body("vatId", is("VAT123456"))
.body("defaultPrefix", is("for"))
- .body("billingContact.label", is(givenContact.getLabel()))
- .body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName()))
+ .body("debitorRel.contact.label", is(givenContact.getLabel()))
+ .body("debitorRel.holder.tradeName", is(givenBillingPerson.getTradeName()))
.body("refundBankAccount.holder", is(givenBankAccount.getHolder()))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
@@ -206,15 +326,23 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
- {
- "partnerUuid": "%s",
- "billingContactUuid": "%s",
- "debitorNumberSuffix": "%s",
- "defaultPrefix": "for",
- "billable": "true",
- "vatReverseCharge": "false"
- }
- """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix))
+ {
+ "debitorRel": {
+ "type": "DEBITOR",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
+ "contactUuid": "%s"
+ },
+ "debitorNumberSuffix": "%s",
+ "defaultPrefix": "for",
+ "billable": "true",
+ "vatReverseCharge": "false"
+ }
+ """.formatted(
+ givenPartner.getPartnerRel().getHolder().getUuid(),
+ givenPartner.getPartnerRel().getHolder().getUuid(),
+ givenContact.getUuid(),
+ ++nextDebitorSuffix))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@@ -222,8 +350,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("billingContact.label", is(givenContact.getLabel()))
- .body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName()))
+ .body("debitorRel.contact.label", is(givenContact.getLabel()))
+ .body("partner.partnerRel.holder.tradeName", is(givenPartner.getPartnerRel().getHolder().getTradeName()))
.body("vatId", equalTo(null))
.body("vatCountryCode", equalTo(null))
.body("vatBusiness", equalTo(false))
@@ -250,19 +378,22 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
- {
- "partnerUuid": "%s",
- "billingContactUuid": "%s",
- "debitorNumberSuffix": "%s",
- "billable": "true",
- "vatId": "VAT123456",
- "vatCountryCode": "DE",
- "vatBusiness": true,
- "vatReverseCharge": "false",
- "defaultPrefix": "thi"
- }
- """
- .formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorSuffix))
+ {
+ "debitorRel": {
+ "type": "DEBITOR",
+ "anchorUuid": "%s",
+ "holderUuid": "%s",
+ "contactUuid": "%s"
+ },
+ "debitorNumberSuffix": "%s",
+ "defaultPrefix": "for",
+ "billable": "true",
+ "vatReverseCharge": "false"
+ }
+ """.formatted(
+ givenPartner.getPartnerRel().getAnchor().getUuid(),
+ givenPartner.getPartnerRel().getAnchor().getUuid(),
+ givenContactUuid, ++nextDebitorSuffix))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@@ -273,10 +404,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
}
@Test
- void globalAdmin_canNotAddDebitor_ifPartnerDoesNotExist() {
+ void globalAdmin_canNotAddDebitor_ifDebitorRelDoesNotExist() {
context.define("superuser-alex@hostsharing.net");
- final var givenPartnerUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
+ final var givenDebitorRelUuid = UUID.fromString("00000000-0000-0000-0000-000000000000");
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off
@@ -284,24 +415,20 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.header("current-user", "superuser-alex@hostsharing.net")
.contentType(ContentType.JSON)
.body("""
- {
- "partnerUuid": "%s",
- "billingContactUuid": "%s",
- "debitorNumberSuffix": "%s",
- "billable": "true",
- "vatId": "VAT123456",
- "vatCountryCode": "DE",
- "vatBusiness": true,
- "vatReverseCharge": "false",
- "defaultPrefix": "for"
- }
- """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix))
+ {
+ "debitorRelUuid": "%s",
+ "debitorNumberSuffix": "%s",
+ "defaultPrefix": "for",
+ "billable": "true",
+ "vatReverseCharge": "false"
+ }
+ """.formatted(givenDebitorRelUuid, ++nextDebitorSuffix))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
.then().log().all().assertThat()
.statusCode(400)
- .body("message", is("Unable to find Partner with uuid 00000000-0000-0000-0000-000000000000"));
+ .body("message", is("Unable to find HsOfficeRelationEntity with uuid 00000000-0000-0000-0000-000000000000"));
// @formatter:on
}
}
@@ -321,14 +448,53 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.port(port)
.when()
.get("http://localhost/api/hs/office/debitors/" + givenDebitorUuid)
- .then().log().body().assertThat()
+ .then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "partner": { person: { "tradeName": "First GmbH" } },
- "billingContact": { "label": "first contact" }
- }
+ "debitorRel": {
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
+ "holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
+ "type": "DEBITOR",
+ "contact": {
+ "label": "first contact",
+ "postalAddress": "\\nVorname Nachname\\nStraße Hnr\\nPLZ Stadt\\n",
+ "emailAddresses": "contact-admin@firstcontact.example.com",
+ "phoneNumbers": "+49 123 1234567"
+ }
+ },
+ "debitorNumber": 1000111,
+ "debitorNumberSuffix": 11,
+ "partner": {
+ "partnerNumber": 10001,
+ "partnerRel": {
+ "anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG"},
+ "holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
+ "type": "PARTNER",
+ "mark": null,
+ "contact": {
+ "label": "first contact",
+ "postalAddress": "\\nVorname Nachname\\nStraße Hnr\\nPLZ Stadt\\n",
+ "emailAddresses": "contact-admin@firstcontact.example.com",
+ "phoneNumbers": "+49 123 1234567"
+ }
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789"
+ }
+ },
+ "billable": true,
+ "vatBusiness": true,
+ "vatReverseCharge": false,
+ "refundBankAccount": {
+ "holder": "First GmbH",
+ "iban": "DE02120300000000202051",
+ "bic": "BYLADEM1001"
+ },
+ "defaultPrefix": "fir"
+ }
""")); // @formatter:on
}
@@ -350,7 +516,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
@Test
@Accepts({ "Debitor:X(Access Control)" })
- void contactAdminUser_canGetRelatedDebitor() {
+ void contactAdminUser_canGetRelatedDebitorExceptRefundBankAccount() {
context.define("superuser-alex@hostsharing.net");
final var givenDebitorUuid = debitorRepo.findDebitorByOptionalNameLike("first contact").get(0).getUuid();
@@ -365,9 +531,10 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "partner": { person: { "tradeName": "First GmbH" } },
- "billingContact": { "label": "first contact" },
- "refundBankAccount": { "holder": "First GmbH" }
+ "debitorNumber": 1000111,
+ "partner": { "partnerNumber": 10001 },
+ "debitorRel": { "contact": { "label": "first contact" } },
+ "refundBankAccount": null
}
""")); // @formatter:on
}
@@ -378,7 +545,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
class PatchDebitor {
@Test
- void globalAdmin_withoutAssumedRole_canPatchAllPropertiesOfArbitraryDebitor() {
+ void globalAdmin_withoutAssumedRole_canPatchArbitraryDebitor() {
context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor();
@@ -400,77 +567,90 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.port(port)
.when()
.patch("http://localhost/api/hs/office/debitors/" + givenDebitor.getUuid())
- .then().assertThat()
+ .then().log().all().assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
- .body("uuid", isUuidValid())
- .body("vatId", is("VAT222222"))
- .body("vatCountryCode", is("AA"))
- .body("vatBusiness", is(true))
- .body("defaultPrefix", is("for"))
- .body("billingContact.label", is(givenContact.getLabel()))
- .body("partner.person.tradeName", is(givenDebitor.getPartner().getPerson().getTradeName()));
+ .body("", lenientlyEquals("""
+ {
+ "debitorRel": {
+ "anchor": { "tradeName": "Fourth eG" },
+ "holder": { "tradeName": "Fourth eG" },
+ "type": "DEBITOR",
+ "mark": null,
+ "contact": { "label": "fourth contact" }
+ },
+ "debitorNumber": 10004${debitorNumberSuffix},
+ "debitorNumberSuffix": ${debitorNumberSuffix},
+ "partner": {
+ "partnerNumber": 10004,
+ "partnerRel": {
+ "anchor": { "tradeName": "Hostsharing eG" },
+ "holder": { "tradeName": "Fourth eG" },
+ "type": "PARTNER",
+ "mark": null,
+ "contact": { "label": "fourth contact" }
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789",
+ "birthName": null,
+ "birthPlace": null,
+ "birthday": null,
+ "dateOfDeath": null
+ }
+ },
+ "billable": true,
+ "vatId": "VAT222222",
+ "vatCountryCode": "AA",
+ "vatBusiness": true,
+ "vatReverseCharge": false,
+ "defaultPrefix": "for"
+ }
+ """
+ .replace("${debitorNumberSuffix}", givenDebitor.getDebitorNumberSuffix().toString()))
+ );
// @formatter:on
// finally, the debitor is actually updated
context.define("superuser-alex@hostsharing.net");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
- .matches(partner -> {
- assertThat(partner.getPartner().getPerson().getTradeName()).isEqualTo(givenDebitor.getPartner()
- .getPerson()
- .getTradeName());
- assertThat(partner.getBillingContact().getLabel()).isEqualTo("fourth contact");
- assertThat(partner.getVatId()).isEqualTo("VAT222222");
- assertThat(partner.getVatCountryCode()).isEqualTo("AA");
- assertThat(partner.isVatBusiness()).isEqualTo(true);
+ .matches(debitor -> {
+ assertThat(debitor.getDebitorRel().getHolder().getTradeName())
+ .isEqualTo(givenDebitor.getDebitorRel().getHolder().getTradeName());
+ assertThat(debitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(debitor.getVatId()).isEqualTo("VAT222222");
+ assertThat(debitor.getVatCountryCode()).isEqualTo("AA");
+ assertThat(debitor.isVatBusiness()).isEqualTo(true);
return true;
});
}
@Test
- void globalAdmin_withoutAssumedRole_canPatchPartialPropertiesOfArbitraryDebitor() {
+ void theContactOwner_canNotPatchARelatedDebitor() {
context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor();
- final var newBillingContact = contactRepo.findContactByOptionalLabelLike("sixth").get(0);
- final var location = RestAssured // @formatter:off
- .given()
+ // @formatter:on
+ RestAssured // @formatter:off
+ .given()
.header("current-user", "superuser-alex@hostsharing.net")
+ .header("assumed-roles", "hs_office_contact#fourthcontact.admin")
.contentType(ContentType.JSON)
.body("""
- {
- "billingContactUuid": "%s",
- "vatId": "VAT999999"
- }
- """.formatted(newBillingContact.getUuid()))
+ {
+ "vatId": "VAT999999"
+ }
+ """)
.port(port)
- .when()
+ .when()
.patch("http://localhost/api/hs/office/debitors/" + givenDebitor.getUuid())
- .then().assertThat()
- .statusCode(200)
- .contentType(ContentType.JSON)
- .body("uuid", isUuidValid())
- .body("billingContact.label", is("sixth contact"))
- .body("vatId", is("VAT999999"))
- .body("vatCountryCode", is(givenDebitor.getVatCountryCode()))
- .body("vatBusiness", is(givenDebitor.isVatBusiness()));
- // @formatter:on
+ .then().log().all().assertThat()
+ .statusCode(403)
+ .body("message", containsString("ERROR: [403] Subject"))
+ .body("message", containsString("is not allowed to update hs_office_debitor uuid "));
- // finally, the debitor is actually updated
- assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
- .matches(partner -> {
- assertThat(partner.getPartner().getPerson().getTradeName()).isEqualTo(givenDebitor.getPartner()
- .getPerson()
- .getTradeName());
- assertThat(partner.getBillingContact().getLabel()).isEqualTo("sixth contact");
- assertThat(partner.getVatId()).isEqualTo("VAT999999");
- assertThat(partner.getVatCountryCode()).isEqualTo(givenDebitor.getVatCountryCode());
- assertThat(partner.isVatBusiness()).isEqualTo(givenDebitor.isVatBusiness());
- return true;
- });
}
-
}
@Nested
@@ -500,7 +680,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
void contactAdminUser_canNotDeleteRelatedDebitor() {
context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor();
- assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off
.given()
@@ -520,7 +700,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
void normalUser_canNotDeleteUnrelatedDebitor() {
context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor();
- assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off
.given()
@@ -544,8 +724,14 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(++nextDebitorSuffix)
.billable(true)
- .partner(givenPartner)
- .billingContact(givenContact)
+ .debitorRel(
+ HsOfficeRelationEntity.builder()
+ .type(DEBITOR)
+ .anchor(givenPartner.getPartnerRel().getHolder())
+ .holder(givenPartner.getPartnerRel().getHolder())
+ .contact(givenContact)
+ .build()
+ )
.defaultPrefix("abc")
.vatReverseCharge(false)
.build();
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java
index 01ea5777..4d826224 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java
@@ -1,9 +1,8 @@
package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
-import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
-import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
@@ -28,9 +27,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
> {
private static final UUID INITIAL_DEBITOR_UUID = UUID.randomUUID();
- private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID();
- private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID();
- private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
+ private static final UUID INITIAL_DEBITOR_REL_UUID = UUID.randomUUID();
+ private static final UUID PATCHED_DEBITOR_REL_UUID = UUID.randomUUID();
private static final String PATCHED_DEFAULT_PREFIX = "xyz";
private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ";
@@ -46,12 +44,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
private static final UUID INITIAL_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID();
private static final UUID PATCHED_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID();
- private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder()
- .uuid(INITIAL_PARTNER_UUID)
- .build();
-
- private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder()
- .uuid(INITIAL_CONTACT_UUID)
+ private final HsOfficeRelationEntity givenInitialDebitorRel = HsOfficeRelationEntity.builder()
+ .uuid(INITIAL_DEBITOR_REL_UUID)
.build();
private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder()
@@ -62,8 +56,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
@BeforeEach
void initMocks() {
- lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation ->
- HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build());
+ lenient().when(em.getReference(eq(HsOfficeRelationEntity.class), any())).thenAnswer(invocation ->
+ HsOfficeRelationEntity.builder().uuid(invocation.getArgument(1)).build());
lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation ->
HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build());
}
@@ -72,8 +66,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
protected HsOfficeDebitorEntity newInitialEntity() {
final var entity = new HsOfficeDebitorEntity();
entity.setUuid(INITIAL_DEBITOR_UUID);
- entity.setPartner(givenInitialPartner);
- entity.setBillingContact(givenInitialContact);
+ entity.setDebitorRel(givenInitialDebitorRel);
entity.setBillable(INITIAL_BILLABLE);
entity.setVatId("initial VAT-ID");
entity.setVatCountryCode("AA");
@@ -98,11 +91,11 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
protected Stream propertyTestDescriptors() {
return Stream.of(
new JsonNullableProperty<>(
- "billingContact",
- HsOfficeDebitorPatchResource::setBillingContactUuid,
- PATCHED_CONTACT_UUID,
- HsOfficeDebitorEntity::setBillingContact,
- newBillingContact(PATCHED_CONTACT_UUID))
+ "debitorRel",
+ HsOfficeDebitorPatchResource::setDebitorRelUuid,
+ PATCHED_DEBITOR_REL_UUID,
+ HsOfficeDebitorEntity::setDebitorRel,
+ newDebitorRel(PATCHED_DEBITOR_REL_UUID))
.notNullable(),
new SimpleProperty<>(
"billable",
@@ -129,7 +122,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
new SimpleProperty<>(
"vatReverseCharge",
HsOfficeDebitorPatchResource::setVatReverseCharge,
- PATCHED_BILLABLE,
+ PATCHED_VAT_REVERSE_CHARGE,
HsOfficeDebitorEntity::setVatReverseCharge)
.notNullable(),
new JsonNullableProperty<>(
@@ -148,15 +141,15 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
);
}
- private HsOfficeContactEntity newBillingContact(final UUID uuid) {
- final var newContact = new HsOfficeContactEntity();
- newContact.setUuid(uuid);
- return newContact;
+ private HsOfficeRelationEntity newDebitorRel(final UUID uuid) {
+ return HsOfficeRelationEntity.builder()
+ .uuid(uuid)
+ .build();
}
private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) {
- final var newBankAccount = new HsOfficeBankAccountEntity();
- newBankAccount.setUuid(uuid);
- return newBankAccount;
+ return HsOfficeBankAccountEntity.builder()
+ .uuid(uuid)
+ .build();
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
index 96f1ba13..3ad1c8ea 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
@@ -1,61 +1,52 @@
package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
-import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class HsOfficeDebitorEntityUnitTest {
+ private HsOfficeRelationEntity givenDebitorRel = HsOfficeRelationEntity.builder()
+ .anchor(HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.LEGAL_PERSON)
+ .tradeName("some partner trade name")
+ .build())
+ .holder(HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.LEGAL_PERSON)
+ .tradeName("some billing trade name")
+ .build())
+ .contact(HsOfficeContactEntity.builder().label("some label").build())
+ .build();
+
@Test
void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)67)
- .partner(HsOfficePartnerEntity.builder()
- .person(HsOfficePersonEntity.builder()
- .personType(HsOfficePersonType.LEGAL_PERSON)
- .tradeName("some trade name")
- .build())
- .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
- .partnerNumber(12345)
- .build())
- .billingContact(HsOfficeContactEntity.builder().label("some label").build())
+ .debitorRel(givenDebitorRel)
.defaultPrefix("som")
- .build();
-
- final var result = given.toString();
-
- assertThat(result).isEqualTo("debitor(D-1234567: LP some trade name: som)");
- }
-
- @Test
- void toStringWithoutPersonContainsDebitorNumber() {
- final var given = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
- .person(null)
- .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
.partnerNumber(12345)
.build())
- .billingContact(HsOfficeContactEntity.builder().label("some label").build())
.build();
final var result = given.toString();
- assertThat(result).isEqualTo("debitor(D-1234567: )");
+ assertThat(result).isEqualTo("debitor(D-1234567: rel(anchor='LP some partner trade name', holder='LP some billing trade name'), som)");
}
@Test
void toShortStringContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder()
+ .debitorRel(givenDebitorRel)
+ .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
- .debitorNumberSuffix((byte)67)
.build();
final var result = given.toShortString();
@@ -66,10 +57,11 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() {
final var given = HsOfficeDebitorEntity.builder()
+ .debitorRel(givenDebitorRel)
+ .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
- .debitorNumberSuffix((byte)67)
.build();
final var result = given.getDebitorNumber();
@@ -80,8 +72,9 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void getDebitorNumberWithoutPartnerReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
- .partner(null)
+ .debitorRel(givenDebitorRel)
.debitorNumberSuffix((byte)67)
+ .partner(null)
.build();
final var result = given.getDebitorNumber();
@@ -92,10 +85,9 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void getDebitorNumberWithoutPartnerNumberReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
- .partner(HsOfficePartnerEntity.builder()
- .partnerNumber(null)
- .build())
+ .debitorRel(givenDebitorRel)
.debitorNumberSuffix((byte)67)
+ .partner(HsOfficePartnerEntity.builder().build())
.build();
final var result = given.getDebitorNumber();
@@ -106,10 +98,11 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
+ .debitorRel(givenDebitorRel)
+ .debitorNumberSuffix(null)
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
- .debitorNumberSuffix(null)
.build();
final var result = given.getDebitorNumber();
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
index 46d0878f..5f53df24 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
@@ -4,11 +4,16 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
+import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt;
+import org.hibernate.Hibernate;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -18,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
+import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
@@ -27,13 +33,14 @@ import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
+import static net.hostsharing.hsadminng.hs.office.test.EntityList.one;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
-@Import( { Context.class, JpaAttempt.class })
+@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class })
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired
@@ -45,6 +52,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired
HsOfficeContactRepository contactRepo;
+ @Autowired
+ HsOfficePersonRepository personRepo;
+
@Autowired
HsOfficeBankAccountRepository bankAccountRepo;
@@ -60,9 +70,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired
JpaAttempt jpaAttempt;
+ @Autowired
+ RbacGrantsDiagramService mermaidService;
+
@MockBean
HttpServletRequest request;
-
@Nested
class CreateDebitor {
@@ -71,15 +83,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given
context("superuser-alex@hostsharing.net");
final var count = debitorRepo.count();
- final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
+ final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
+ final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
// when
final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)21)
- .partner(givenPartner)
- .billingContact(givenContact)
+ .debitorRel(HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(givenPartnerPerson)
+ .holder(givenPartnerPerson)
+ .contact(givenContact)
+ .build())
.defaultPrefix("abc")
.billable(false)
.build();
@@ -99,16 +115,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) {
// given
context("superuser-alex@hostsharing.net");
- final var count = debitorRepo.count();
- final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
+ final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
+ final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
// when
final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)21)
- .partner(givenPartner)
- .billingContact(givenContact)
+ .debitorRel(HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(givenPartnerPerson)
+ .holder(givenPartnerPerson)
+ .contact(givenContact)
+ .build())
.billable(true)
.vatReverseCharge(false)
.vatBusiness(false)
@@ -128,21 +147,22 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
// some search+replace to make the output fit into the screen width
- .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
- .map(s -> s.replace("22FourtheG-fourthcontact", "FeG"))
- .map(s -> s.replace("FourtheG-fourthcontact", "FeG"))
- .map(s -> s.replace("fourthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.toList();
// when
attempt(em, () -> {
- final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
+ final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
+ final var givenDebitorPerson = one(personRepo.findPersonByOptionalNameLike("Fourth eG"));
+ final var givenContact = one(contactRepo.findContactByOptionalLabelLike("fourth contact"));
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)22)
- .partner(givenPartner)
- .billingContact(givenContact)
+ .debitorRel(HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(givenPartnerPerson)
+ .holder(givenDebitorPerson)
+ .contact(givenContact)
+ .build())
.defaultPrefix("abc")
.billable(false)
.build();
@@ -152,49 +172,52 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_debitor#1000422:FourtheG-fourthcontact.owner",
- "hs_office_debitor#1000422:FourtheG-fourthcontact.admin",
- "hs_office_debitor#1000422:FourtheG-fourthcontact.agent",
- "hs_office_debitor#1000422:FourtheG-fourthcontact.tenant",
- "hs_office_debitor#1000422:FourtheG-fourthcontact.guest"));
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.owner",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.admin",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.agent",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.tenant"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
- .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
- .map(s -> s.replace("22FourtheG-fourthcontact", "FeG"))
- .map(s -> s.replace("FourtheG-fourthcontact", "FeG"))
- .map(s -> s.replace("fourthcontact", "4th"))
- .map(s -> s.replace("hs_office_", ""))
- .containsExactlyInAnyOrder(Array.fromFormatted(
- initialGrantNames,
- // owner
- "{ grant perm DELETE on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
- "{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }",
- "{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
+ .map(s -> s.replace("hs_office_", ""))
+ .containsExactlyInAnyOrder(Array.fromFormatted(
+ initialGrantNames,
+ "{ grant perm INSERT into sepamandate with relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
- // admin
- "{ grant perm UPDATE on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
- "{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
+ // owner
+ "{ grant perm DELETE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
+ "{ grant perm DELETE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to role global#global.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to user superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG.owner and assume }",
- // agent
- "{ grant role debitor#1000422:FeG.agent to role debitor#1000422:FeG.admin by system and assume }",
- "{ grant role debitor#1000422:FeG.agent to role contact#4th.admin by system and assume }",
- "{ grant role debitor#1000422:FeG.agent to role partner#10004:FeG.admin by system and assume }",
+ // admin
+ "{ grant perm UPDATE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
+ "{ grant perm UPDATE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role person#FirstGmbH.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
- // tenant
- "{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }",
- "{ grant role debitor#1000422:FeG.tenant to role debitor#1000422:FeG.agent by system and assume }",
- "{ grant role debitor#1000422:FeG.tenant to role partner#10004:FeG.agent by system and assume }",
- "{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
+ // agent
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role person#FourtheG.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
- // guest
- "{ grant perm SELECT on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
- "{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
+ // tenant
+ "{ grant perm SELECT on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
+ "{ grant perm SELECT on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
+ "{ grant role contact#fourthcontact.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
+ "{ grant role person#FirstGmbH.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
+ "{ grant role person#FourtheG.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role contact#fourthcontact.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role person#FourtheG.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
- null));
+ null));
}
private void assertThatDebitorIsPersisted(final HsOfficeDebitorEntity saved) {
+ final var savedRefreshed = refresh(saved);
final var found = debitorRepo.findByUuid(saved.getUuid());
- assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
+ assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(savedRefreshed);
}
}
@@ -212,9 +235,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
allTheseDebitorsAreReturned(
result,
- "debitor(D-1000111: LP First GmbH: fir)",
- "debitor(D-1000212: LP Second e.K.: sec)",
- "debitor(D-1000313: IF Third OHG: thi)");
+ "debitor(D-1000111: rel(anchor='LP First GmbH', type='DEBITOR', holder='LP First GmbH'), fir)",
+ "debitor(D-1000212: rel(anchor='LP Second e.K.', type='DEBITOR', holder='LP Second e.K.'), sec)",
+ "debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
}
@ParameterizedTest
@@ -233,8 +256,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then:
exactlyTheseDebitorsAreReturned(result,
- "debitor(D-1000111: LP First GmbH: fir)",
- "debitor(D-1000120: LP First GmbH: fif)");
+ "debitor(D-1000111: P-10001, fir)",
+ "debitor(D-1000120: P-10001, fif)");
}
@Test
@@ -262,7 +285,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var result = debitorRepo.findDebitorByDebitorNumber(1000313);
// then
- exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: IF Third OHG: thi)");
+ exactlyTheseDebitorsAreReturned(result,
+ "debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
}
}
@@ -278,7 +302,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var result = debitorRepo.findDebitorByOptionalNameLike("third contact");
// then
- exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: IF Third OHG: thi)");
+ exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: rel(anchor='IF Third OHG', type='DEBITOR', holder='IF Third OHG'), thi)");
}
}
@@ -290,13 +314,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
+
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_partner#10004:FourtheG-fourthcontact.admin");
- assertThatDebitorActuallyInDatabase(givenDebitor);
- final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
- final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
- final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
+ final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
+ final var givenNewBillingPerson = one(personRepo.findPersonByOptionalNameLike("Firby"));
+ final var givenNewContact = one(contactRepo.findContactByOptionalLabelLike("sixth contact"));
+ final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
final String givenNewVatId = "NEW-VAT-ID";
final String givenNewVatCountryCode = "NC";
final boolean givenNewVatBusiness = !givenDebitor.isVatBusiness();
@@ -304,8 +329,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- givenDebitor.setPartner(givenNewPartner);
- givenDebitor.setBillingContact(givenNewContact);
+ givenDebitor.setDebitorRel(HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(givenNewPartnerPerson)
+ .holder(givenNewBillingPerson)
+ .contact(givenNewContact)
+ .build());
givenDebitor.setRefundBankAccount(givenNewBankAccount);
givenDebitor.setVatId(givenNewVatId);
givenDebitor.setVatCountryCode(givenNewVatCountryCode);
@@ -317,15 +346,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin");
+ "global#global.admin", true);
// ... partner role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_partner#10004:FourtheG-fourthcontact.agent");
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_partner#10001:FirstGmbH-firstcontact.agent");
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan.agent", true);
// ... contact role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
@@ -333,15 +362,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"hs_office_contact#fifthcontact.admin");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_contact#sixthcontact.admin");
+ "hs_office_contact#sixthcontact.admin", false);
// ... bank-account role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#FourtheG.admin");
+ "hs_office_bankaccount#DE02200505501015871393.admin");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#FirstGmbH.admin");
+ "hs_office_bankaccount#DE02120300000000202051.admin", true);
}
@Test
@@ -351,9 +380,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_partner#10004:FourtheG-fourthcontact.admin");
- assertThatDebitorActuallyInDatabase(givenDebitor);
- final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
+ assertThatDebitorActuallyInDatabase(givenDebitor, true);
+ final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
// when
final var result = jpaAttempt.transacted(() -> {
@@ -366,12 +395,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin");
+ "global#global.admin", true);
// ... bank-account role was assigned:
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#FirstGmbH.admin");
+ "hs_office_bankaccount#DE02120300000000202051.admin", true);
}
@Test
@@ -381,8 +410,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fih");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_partner#10004:FourtheG-fourthcontact.admin");
- assertThatDebitorActuallyInDatabase(givenDebitor);
+ "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
+ assertThatDebitorActuallyInDatabase(givenDebitor, true);
// when
final var result = jpaAttempt.transacted(() -> {
@@ -395,34 +424,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin");
+ "global#global.admin", true);
// ... bank-account role was removed from previous bank-account admin:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#FourtheG.admin");
+ "hs_office_bankaccount#DE02200505501015871393.admin");
}
@Test
- public void partnerAdmin_canNotUpdateRelatedDebitor() {
+ public void partnerAgent_canNotUpdateRelatedDebitor() {
// given
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_partner#10004:FourtheG-fourthcontact.admin");
- assertThatDebitorActuallyInDatabase(givenDebitor);
+ "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
+ assertThatDebitorActuallyInDatabase(givenDebitor, true);
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_partner#10004:FourtheG-fourthcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent");
givenDebitor.setVatId("NEW-VAT-ID");
return toCleanup(debitorRepo.save(givenDebitor));
});
// then
- result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] Subject ", " is not allowed to update hs_office_debitor uuid");
+ result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
+ "[403] Subject ", " is not allowed to update hs_office_debitor uuid");
}
@Test
@@ -430,10 +459,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth", "nin");
+ assertThatDebitorActuallyInDatabase(givenDebitor, true);
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_contact#ninthcontact.admin");
- assertThatDebitorActuallyInDatabase(givenDebitor);
+ "hs_office_contact#ninthcontact.admin", false);
// when
final var result = jpaAttempt.transacted(() -> {
@@ -443,22 +472,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
});
// then
- result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] Subject ", " is not allowed to update hs_office_debitor uuid");
+ result.assertExceptionWithRootCauseMessage(
+ JpaObjectRetrievalFailureException.class,
+ // this technical error message gets translated to a [403] error at the controller level
+ "Unable to find net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity with id ");
}
- private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved) {
+ private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved, final boolean withPartner) {
final var found = debitorRepo.findByUuid(saved.getUuid());
- assertThat(found).isNotEmpty().get().isNotSameAs(saved)
- .extracting(Object::toString).isEqualTo(saved.toString());
+ assertThat(found).isNotEmpty();
+ found.ifPresent(foundEntity -> {
+ em.refresh(foundEntity);
+ Hibernate.initialize(foundEntity);
+ assertThat(foundEntity).isNotSameAs(saved);
+ if (withPartner) {
+ assertThat(foundEntity.getPartner()).isNotNull();
+ }
+ assertThat(foundEntity.getDebitorRel()).extracting(HsOfficeRelationEntity::toString)
+ .isEqualTo(saved.getDebitorRel().toString());
+ });
}
private void assertThatDebitorIsVisibleForUserWithRole(
final HsOfficeDebitorEntity entity,
- final String assumedRoles) {
+ final String assumedRoles,
+ final boolean withPartner) {
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles);
- assertThatDebitorActuallyInDatabase(entity);
+ assertThatDebitorActuallyInDatabase(entity, withPartner);
}).assertSuccessful();
}
@@ -497,14 +538,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
}
@Test
- public void relatedPerson_canNotDeleteTheirRelatedDebitor() {
+ public void debitorAgent_canViewButNotDeleteTheirRelatedDebitor() {
// given
context("superuser-alex@hostsharing.net", null);
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele");
// when
final var result = jpaAttempt.transacted(() -> {
- context("person-FourtheG@example.com");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent();
debitorRepo.deleteByUuid(givenDebitor.getUuid());
@@ -561,20 +602,24 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
}
private HsOfficeDebitorEntity givenSomeTemporaryDebitor(
- final String partner,
- final String contact,
- final String bankAccount,
+ final String partnerName,
+ final String contactLabel,
+ final String bankAccountHolder,
final String defaultPrefix) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partner).get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
+ final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike(partnerName));
+ final var givenContact = one(contactRepo.findContactByOptionalLabelLike(contactLabel));
final var givenBankAccount =
- bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null;
+ bankAccountHolder != null ? one(bankAccountRepo.findByOptionalHolderLike(bankAccountHolder)) : null;
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)20)
- .partner(givenPartner)
- .billingContact(givenContact)
+ .debitorRel(HsOfficeRelationEntity.builder()
+ .type(HsOfficeRelationType.DEBITOR)
+ .anchor(givenPartnerPerson)
+ .holder(givenPartnerPerson)
+ .contact(givenContact)
+ .build())
.refundBankAccount(givenBankAccount)
.defaultPrefix(defaultPrefix)
.billable(true)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
index 36b3d534..2970ea1b 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
@@ -1,7 +1,8 @@
package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.experimental.UtilityClass;
-
+import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
@@ -13,7 +14,11 @@ public class TestHsOfficeDebitor {
public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
+ .debitorRel(HsOfficeRelationEntity.builder()
+ .holder(HsOfficePersonEntity.builder().build())
+ .anchor(HsOfficePersonEntity.builder().build())
+ .contact(TEST_CONTACT)
+ .build())
.partner(TEST_PARTNER)
- .billingContact(TEST_CONTACT)
.build();
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
index 7574d8b2..c0d69951 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
@@ -5,7 +5,6 @@ import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.HsadminNgApplication;
import net.hostsharing.hsadminng.context.Context;
-import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.test.Accepts;
@@ -24,6 +23,7 @@ import jakarta.persistence.PersistenceContext;
import java.time.LocalDate;
import java.util.UUID;
+import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.CANCELLATION;
import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.NONE;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
@@ -51,9 +51,6 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
@Autowired
HsOfficeMembershipRepository membershipRepo;
- @Autowired
- HsOfficeDebitorRepository debitorRepo;
-
@Autowired
HsOfficePartnerRepository partnerRepo;
@@ -82,8 +79,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body("", lenientlyEquals("""
[
{
- "partner": { "person": { "tradeName": "First GmbH" } },
- "mainDebitor": { "debitorNumber": 1000111 },
+ "partner": { "partnerNumber": 10001 },
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
@@ -91,8 +87,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"reasonForTermination": "NONE"
},
{
- "partner": { "person": { "tradeName": "Second e.K." } },
- "mainDebitor": { "debitorNumber": 1000212 },
+ "partner": { "partnerNumber": 10002 },
"memberNumber": 1000202,
"memberNumberSuffix": "02",
"validFrom": "2022-10-01",
@@ -100,8 +95,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"reasonForTermination": "NONE"
},
{
- "partner": { "person": { "tradeName": "Third OHG" } },
- "mainDebitor": { "debitorNumber": 1000313 },
+ "partner": { "partnerNumber": 10003 },
"memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01",
@@ -132,8 +126,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body("", lenientlyEquals("""
[
{
- "partner": { "person": { "tradeName": "First GmbH" } },
- "mainDebitor": { "debitorNumber": 1000111 },
+ "partner": { "partnerNumber": 10001 },
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
@@ -161,8 +154,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body("", lenientlyEquals("""
[
{
- "partner": { "person": { "tradeName": "Second e.K." } },
- "mainDebitor": { "debitorNumber": 1000212 },
+ "partner": { "partnerNumber": 10002 },
"memberNumber": 1000202,
"memberNumberSuffix": "02",
"validFrom": "2022-10-01",
@@ -184,7 +176,6 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
context.define("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0);
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0);
final var givenMemberSuffix = TEMP_MEMBER_NUMBER_SUFFIX;
final var expectedMemberNumber = Integer.parseInt(givenPartner.getPartnerNumber() + TEMP_MEMBER_NUMBER_SUFFIX);
@@ -195,12 +186,11 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body("""
{
"partnerUuid": "%s",
- "mainDebitorUuid": "%s",
"memberNumberSuffix": "%s",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
- """.formatted(givenPartner.getUuid(), givenDebitor.getUuid(), givenMemberSuffix))
+ """.formatted(givenPartner.getUuid(), givenMemberSuffix))
.port(port)
.when()
.post("http://localhost/api/hs/office/memberships")
@@ -208,9 +198,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber()))
- .body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix()))
- .body("partner.person.tradeName", is("Third OHG"))
+ .body("partner.partnerNumber", is(10003))
.body("memberNumber", is(expectedMemberNumber))
.body("memberNumberSuffix", is(givenMemberSuffix))
.body("validFrom", is("2022-10-13"))
@@ -246,8 +234,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "partner": { "person": { "tradeName": "First GmbH" } },
- "mainDebitor": { "debitorNumber": 1000111 },
+ "partner": { "partnerNumber": 10001 },
"memberNumber": 1000101,
"memberNumberSuffix": "01",
"validFrom": "2022-10-01",
@@ -275,14 +262,14 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
@Test
@Accepts({ "Membership:X(Access Control)" })
- void debitorAgentUser_canGetRelatedMembership() {
+ void parnerRelAgent_canGetRelatedMembership() {
context.define("superuser-alex@hostsharing.net");
final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid();
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.agent")
+ .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-ThirdOHG.agent")
.port(port)
.when()
.get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid)
@@ -291,11 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "partner": { "person": { "tradeName": "Third OHG" } },
- "mainDebitor": {
- "debitorNumber": 1000313,
- "billingContact": { "label": "third contact" }
- },
+ "partner": { "partnerNumber": 10003 },
"memberNumber": 1000303,
"memberNumberSuffix": "03",
"validFrom": "2022-10-01",
@@ -314,7 +297,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
void globalAdmin_canPatchValidToOfArbitraryMembership() {
context.define("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
+ final var givenMembership = givenSomeTemporaryMembershipBessler("First");
final var location = RestAssured // @formatter:off
.given()
@@ -333,10 +316,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
- .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber()))
- .body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix()))
- .body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix()))
+ .body("partner.partnerNumber", is(givenMembership.getPartner().getPartnerNumber()))
.body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix()))
.body("validFrom", is("2022-11-01"))
.body("validTo", is("2023-12-31"))
@@ -346,72 +326,31 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
// finally, the Membership is actually updated
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
.matches(mandate -> {
- assertThat(mandate.getPartner().toShortString()).isEqualTo("LP First GmbH");
- assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString());
+ assertThat(mandate.getPartner().toShortString()).isEqualTo("P-10001");
assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix());
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)");
- assertThat(mandate.getReasonForTermination()).isEqualTo(HsOfficeReasonForTermination.CANCELLATION);
+ assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION);
return true;
});
}
@Test
- void globalAdmin_canPatchMainDebitorOfArbitraryMembership() {
+ void partnerRelAgent_canPatchValidityOfRelatedMembership() {
- context.define("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
- final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(1000313).get(0);
+ // given
+ final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent";
+ context.define("superuser-alex@hostsharing.net", givenPartnerAgent);
+ final var givenMembership = givenSomeTemporaryMembershipBessler("First");
+ // when
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
+ .header("assumed-roles", givenPartnerAgent)
.contentType(ContentType.JSON)
.body("""
{
- "mainDebitorUuid": "%s"
- }
- """.formatted(givenNewMainDebitor.getUuid()))
- .port(port)
- .when()
- .patch("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
- .then().log().all().assertThat()
- .statusCode(200)
- .contentType(ContentType.JSON)
- .body("uuid", isUuidValid())
- .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
- .body("mainDebitor.debitorNumber", is(1000313))
- .body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix()))
- .body("validFrom", is("2022-11-01"))
- .body("validTo", nullValue())
- .body("reasonForTermination", is("NONE"));
- // @formatter:on
-
- // finally, the Membership is actually updated
- assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
- .matches(mandate -> {
- assertThat(mandate.getPartner().toShortString()).isEqualTo("LP First GmbH");
- assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString());
- assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix());
- assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)");
- assertThat(mandate.getReasonForTermination()).isEqualTo(NONE);
- return true;
- });
- }
-
- @Test
- void partnerAgent_canViewButNotPatchValidityOfRelatedMembership() {
-
- context.define("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.agent");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
-
- final var location = RestAssured // @formatter:off
- .given()
- .header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.agent")
- .contentType(ContentType.JSON)
- .body("""
- {
- "validTo": "2023-12-31",
+ "validTo": "2024-01-01",
"reasonForTermination": "CANCELLATION"
}
""")
@@ -419,13 +358,13 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.when()
.patch("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
.then().assertThat()
- .statusCode(403); // @formatter:on
+ .statusCode(200); // @formatter:on
// finally, the Membership is actually updated
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
.matches(mandate -> {
- assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)");
- assertThat(mandate.getReasonForTermination()).isEqualTo(NONE);
+ assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-02)");
+ assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION);
return true;
});
}
@@ -438,7 +377,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
@Test
void globalAdmin_canDeleteArbitraryMembership() {
context.define("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
+ final var givenMembership = givenSomeTemporaryMembershipBessler("First");
RestAssured // @formatter:off
.given()
@@ -457,12 +396,12 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
@Accepts({ "Membership:X(Access Control)" })
void partnerAgentUser_canNotDeleteRelatedMembership() {
context.define("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
+ final var givenMembership = givenSomeTemporaryMembershipBessler("First");
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.admin")
+ .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent")
.port(port)
.when()
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
@@ -477,7 +416,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
@Accepts({ "Membership:X(Access Control)" })
void normalUser_canNotDeleteUnrelatedMembership() {
context.define("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembershipBessler();
+ final var givenMembership = givenSomeTemporaryMembershipBessler("First");
RestAssured // @formatter:off
.given()
@@ -493,15 +432,13 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
}
}
- private HsOfficeMembershipEntity givenSomeTemporaryMembershipBessler() {
+ private HsOfficeMembershipEntity givenSomeTemporaryMembershipBessler(final String partnerName) {
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
- final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
+ final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerName).get(0);
final var newMembership = HsOfficeMembershipEntity.builder()
.uuid(UUID.randomUUID())
.partner(givenPartner)
- .mainDebitor(givenDebitor)
.memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX)
.validity(Range.closedInfinite(LocalDate.parse("2022-11-01")))
.reasonForTermination(NONE)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java
index 63ea7306..bcd7e9ab 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java
@@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.membership;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionRepository;
-import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.mapper.Mapper;
import org.junit.jupiter.api.BeforeEach;
@@ -28,7 +27,6 @@ import java.util.UUID;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -76,7 +74,6 @@ public class HsOfficeMembershipControllerRestTest {
.content("""
{
"partnerUuid": null,
- "mainDebitorUuid": "%s",
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
@@ -91,40 +88,12 @@ public class HsOfficeMembershipControllerRestTest {
.andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]")));
}
- @Test
- void respondBadRequest_ifDebitorUuidIsMissing() throws Exception {
-
- // when
- mockMvc.perform(MockMvcRequestBuilders
- .post("/api/hs/office/memberships")
- .header("current-user", "superuser-alex@hostsharing.net")
- .contentType(MediaType.APPLICATION_JSON)
- .content("""
- {
- "partnerUuid": "%s",
- "mainDebitorUuid": null,
- "memberNumberSuffix": "01",
- "validFrom": "2022-10-13",
- "membershipFeeBillable": "true"
- }
- """.formatted(UUID.randomUUID()))
- .accept(MediaType.APPLICATION_JSON))
-
- // then
- .andExpect(status().is4xxClientError())
- .andExpect(jsonPath("statusCode", is(400)))
- .andExpect(jsonPath("statusPhrase", is("Bad Request")))
- .andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]")));
- }
-
@Test
void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception {
// given
final var givenPartnerUuid = UUID.randomUUID();
- final var givenMainDebitorUuid = UUID.randomUUID();
when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null);
- when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class));
// when
mockMvc.perform(MockMvcRequestBuilders
@@ -134,12 +103,11 @@ public class HsOfficeMembershipControllerRestTest {
.content("""
{
"partnerUuid": "%s",
- "mainDebitorUuid": "%s",
"memberNumberSuffix": "01",
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
- """.formatted(givenPartnerUuid, givenMainDebitorUuid))
+ """.formatted(givenPartnerUuid))
.accept(MediaType.APPLICATION_JSON))
// then
@@ -149,38 +117,6 @@ public class HsOfficeMembershipControllerRestTest {
.andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid)));
}
- @Test
- void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception {
-
- // given
- final var givenPartnerUuid = UUID.randomUUID();
- final var givenMainDebitorUuid = UUID.randomUUID();
- when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class));
- when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null);
-
- // when
- mockMvc.perform(MockMvcRequestBuilders
- .post("/api/hs/office/memberships")
- .header("current-user", "superuser-alex@hostsharing.net")
- .contentType(MediaType.APPLICATION_JSON)
- .content("""
- {
- "partnerUuid": "%s",
- "mainDebitorUuid": "%s",
- "memberNumberSuffix": "01",
- "validFrom": "2022-10-13",
- "membershipFeeBillable": "true"
- }
- """.formatted(givenPartnerUuid, givenMainDebitorUuid))
- .accept(MediaType.APPLICATION_JSON))
-
- // then
- .andExpect(status().is4xxClientError())
- .andExpect(jsonPath("statusCode", is(400)))
- .andExpect(jsonPath("statusPhrase", is("Bad Request")))
- .andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid)));
- }
-
@ParameterizedTest
@EnumSource(InvalidMemberSuffixVariants.class)
void respondBadRequest_ifMemberNumberSuffixIsInvalid(final InvalidMemberSuffixVariants testCase) throws Exception {
@@ -193,12 +129,11 @@ public class HsOfficeMembershipControllerRestTest {
.content("""
{
"partnerUuid": "%s",
- "mainDebitorUuid": "%s",
%s
"validFrom": "2022-10-13",
"membershipFeeBillable": "true"
}
- """.formatted(UUID.randomUUID(), UUID.randomUUID(), testCase.memberNumberSuffixEntry))
+ """.formatted(UUID.randomUUID(), testCase.memberNumberSuffixEntry))
.accept(MediaType.APPLICATION_JSON))
// then
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
index ee4944c1..b691095b 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
@@ -17,7 +17,6 @@ import java.time.LocalDate;
import java.util.UUID;
import java.util.stream.Stream;
-import static net.hostsharing.hsadminng.hs.office.debitor.TestHsOfficeDebitor.TEST_DEBITOR;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.mockito.ArgumentMatchers.any;
@@ -32,7 +31,6 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
> {
private static final UUID INITIAL_MEMBERSHIP_UUID = UUID.randomUUID();
- private static final UUID PATCHED_MAIN_DEBITOR_UUID = UUID.randomUUID();
private static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-04-15");
private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31");
@@ -56,7 +54,6 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
protected HsOfficeMembershipEntity newInitialEntity() {
final var entity = new HsOfficeMembershipEntity();
entity.setUuid(INITIAL_MEMBERSHIP_UUID);
- entity.setMainDebitor(TEST_DEBITOR);
entity.setPartner(TEST_PARTNER);
entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM));
entity.setMembershipFeeBillable(GIVEN_MEMBERSHIP_FEE_BILLABLE);
@@ -70,19 +67,12 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
@Override
protected HsOfficeMembershipEntityPatcher createPatcher(final HsOfficeMembershipEntity membership) {
- return new HsOfficeMembershipEntityPatcher(em, mapper, membership);
+ return new HsOfficeMembershipEntityPatcher(mapper, membership);
}
@Override
protected Stream propertyTestDescriptors() {
return Stream.of(
- new JsonNullableProperty<>(
- "debitor",
- HsOfficeMembershipPatchResource::setMainDebitorUuid,
- PATCHED_MAIN_DEBITOR_UUID,
- HsOfficeMembershipEntity::setMainDebitor,
- newDebitor(PATCHED_MAIN_DEBITOR_UUID))
- .notNullable(),
new JsonNullableProperty<>(
"valid",
HsOfficeMembershipPatchResource::setValidTo,
@@ -102,10 +92,4 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficeMembershipEntity::setMembershipFeeBillable)
);
}
-
- private static HsOfficeDebitorEntity newDebitor(final UUID uuid) {
- final var newDebitor = new HsOfficeDebitorEntity();
- newDebitor.setUuid(uuid);
- return newDebitor;
- }
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
index b1815755..1c4d2dc6 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
@@ -9,7 +9,6 @@ import java.lang.reflect.InvocationTargetException;
import java.time.LocalDate;
import java.util.Arrays;
-import static net.hostsharing.hsadminng.hs.office.debitor.TestHsOfficeDebitor.TEST_DEBITOR;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
import static org.assertj.core.api.Assertions.assertThat;
@@ -20,14 +19,13 @@ class HsOfficeMembershipEntityUnitTest {
final HsOfficeMembershipEntity givenMembership = HsOfficeMembershipEntity.builder()
.memberNumberSuffix("01")
.partner(TEST_PARTNER)
- .mainDebitor(TEST_DEBITOR)
.validity(Range.closedInfinite(GIVEN_VALID_FROM))
.build();
@Test
void toStringContainsAllProps() {
final var result = givenMembership.toString();
- assertThat(result).isEqualTo("Membership(M-1000101, LP Test Ltd., D-1000100, [2020-01-01,))");
+ assertThat(result).isEqualTo("Membership(M-1000101, P-10001, [2020-01-01,))");
}
@Test
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
index 4483304a..a53b2705 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
@@ -28,6 +28,7 @@ import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distin
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
+
@DataJpaTest
@Import( { Context.class, JpaAttempt.class })
class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@@ -65,14 +66,12 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
context("superuser-alex@hostsharing.net");
final var count = membershipRepo.count();
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
// when
final var result = attempt(em, () -> {
final var newMembership = HsOfficeMembershipEntity.builder()
.memberNumberSuffix("11")
.partner(givenPartner)
- .mainDebitor(givenDebitor)
.validity(Range.closedInfinite(LocalDate.parse("2020-01-01")))
.membershipFeeBillable(true)
.build();
@@ -99,11 +98,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// when
attempt(em, () -> {
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
final var newMembership = HsOfficeMembershipEntity.builder()
.memberNumberSuffix("17")
.partner(givenPartner)
- .mainDebitor(givenDebitor)
.validity(Range.closedInfinite(LocalDate.parse("2020-01-01")))
.membershipFeeBillable(true)
.build();
@@ -114,11 +111,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_membership#1000117:FirstGmbH-firstcontact.admin",
- "hs_office_membership#1000117:FirstGmbH-firstcontact.agent",
- "hs_office_membership#1000117:FirstGmbH-firstcontact.guest",
- "hs_office_membership#1000117:FirstGmbH-firstcontact.owner",
- "hs_office_membership#1000117:FirstGmbH-firstcontact.tenant"));
+ "hs_office_membership#M-1000117.admin",
+ "hs_office_membership#M-1000117.owner",
+ "hs_office_membership#M-1000117.referrer"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
@@ -126,33 +121,21 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
initialGrantNames,
// owner
- "{ grant perm DELETE on membership#1000117:First to role membership#1000117:First.owner by system and assume }",
- "{ grant role membership#1000117:First.owner to role global#global.admin by system and assume }",
+ "{ grant perm DELETE on membership#M-1000117 to role membership#M-1000117.owner by system and assume }",
// admin
- "{ grant perm UPDATE on membership#1000117:First to role membership#1000117:First.admin by system and assume }",
- "{ grant role membership#1000117:First.admin to role membership#1000117:First.owner by system and assume }",
+ "{ grant perm UPDATE on membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
+ "{ grant role membership#M-1000117.admin to role membership#M-1000117.owner by system and assume }",
+ "{ grant role membership#M-1000117.owner to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
+ "{ grant role membership#M-1000117.owner to user superuser-alex@hostsharing.net by membership#M-1000117.owner and assume }",
// agent
- "{ grant role membership#1000117:First.agent to role membership#1000117:First.admin by system and assume }",
- "{ grant role partner#10001:First.tenant to role membership#1000117:First.agent by system and assume }",
- "{ grant role membership#1000117:First.agent to role debitor#1000111:First.admin by system and assume }",
- "{ grant role membership#1000117:First.agent to role partner#10001:First.admin by system and assume }",
- "{ grant role debitor#1000111:First.tenant to role membership#1000117:First.agent by system and assume }",
+ "{ grant role membership#M-1000117.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
- // tenant
- "{ grant role membership#1000117:First.tenant to role membership#1000117:First.agent by system and assume }",
- "{ grant role partner#10001:First.guest to role membership#1000117:First.tenant by system and assume }",
- "{ grant role debitor#1000111:First.guest to role membership#1000117:First.tenant by system and assume }",
- "{ grant role membership#1000117:First.tenant to role debitor#1000111:First.agent by system and assume }",
-
- "{ grant role membership#1000117:First.tenant to role partner#10001:First.agent by system and assume }",
-
- // guest
- "{ grant perm SELECT on membership#1000117:First to role membership#1000117:First.guest by system and assume }",
- "{ grant role membership#1000117:First.guest to role membership#1000117:First.tenant by system and assume }",
- "{ grant role membership#1000117:First.guest to role partner#10001:First.tenant by system and assume }",
- "{ grant role membership#1000117:First.guest to role debitor#1000111:First.tenant by system and assume }",
+ // referrer
+ "{ grant perm SELECT on membership#M-1000117 to role membership#M-1000117.referrer by system and assume }",
+ "{ grant role membership#M-1000117.referrer to role membership#M-1000117.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role membership#M-1000117.referrer by system and assume }",
null));
}
@@ -177,9 +160,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// then
exactlyTheseMembershipsAreReturned(
result,
- "Membership(M-1000101, LP First GmbH, D-1000111, [2022-10-01,), NONE)",
- "Membership(M-1000202, LP Second e.K., D-1000212, [2022-10-01,), NONE)",
- "Membership(M-1000303, IF Third OHG, D-1000313, [2022-10-01,), NONE)");
+ "Membership(M-1000101, P-10001, [2022-10-01,), NONE)",
+ "Membership(M-1000202, P-10002, [2022-10-01,), NONE)",
+ "Membership(M-1000303, P-10003, [2022-10-01,), NONE)");
}
@Test
@@ -193,7 +176,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// then
exactlyTheseMembershipsAreReturned(result,
- "Membership(M-1000101, LP First GmbH, D-1000111, [2022-10-01,), NONE)");
+ "Membership(M-1000101, P-10001, [2022-10-01,), NONE)");
}
@Test
@@ -208,7 +191,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
assertThat(result)
.isNotNull()
.extracting(Object::toString)
- .isEqualTo("Membership(M-1000202, LP Second e.K., D-1000212, [2022-10-01,), NONE)");
+ .isEqualTo("Membership(M-1000202, P-10002, [2022-10-01,), NONE)");
}
}
@@ -219,10 +202,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
public void globalAdmin_canUpdateValidityOfArbitraryMembership() {
// given
context("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembership("First", "First", "11");
- assertThatMembershipIsVisibleForUserWithRole(
- givenMembership,
- "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
+ final var givenMembership = givenSomeTemporaryMembership("First", "11");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now();
@@ -243,21 +223,22 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
}
@Test
- public void debitorAdmin_canViewButNotUpdateRelatedMembership() {
+ public void membershipReferrer_canViewButNotUpdateRelatedMembership() {
// given
context("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembership("First", "First", "13");
- assertThatMembershipIsVisibleForUserWithRole(
- givenMembership,
- "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
+ final var givenMembership = givenSomeTemporaryMembership("First", "13");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
+ assertThatMembershipIsVisibleForRole(
+ givenMembership,
+ "hs_office_membership#M-1000113.referrer");
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
- givenMembership.setValidity(Range.closedOpen(
- givenMembership.getValidity().lower(), newValidityEnd));
+ // TODO: we should test with debitor- and partner-admin as well
+ context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113.referrer");
+ givenMembership.setValidity(
+ Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);
});
@@ -272,7 +253,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
.extracting(Object::toString).isEqualTo(saved.toString());
}
- private void assertThatMembershipIsVisibleForUserWithRole(
+ private void assertThatMembershipIsVisibleForRole(
final HsOfficeMembershipEntity entity,
final String assumedRoles) {
jpaAttempt.transacted(() -> {
@@ -280,16 +261,6 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
assertThatMembershipExistsAndIsAccessibleToCurrentContext(entity);
}).assertSuccessful();
}
-
- private void assertThatMembershipIsNotVisibleForUserWithRole(
- final HsOfficeMembershipEntity entity,
- final String assumedRoles) {
- jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", assumedRoles);
- final var found = membershipRepo.findByUuid(entity.getUuid());
- assertThat(found).isEmpty();
- }).assertSuccessful();
- }
}
@Nested
@@ -299,7 +270,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
public void globalAdmin_withoutAssumedRole_canDeleteAnyMembership() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenMembership = givenSomeTemporaryMembership("First", "Second", "12");
+ final var givenMembership = givenSomeTemporaryMembership("First", "12");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -316,14 +287,14 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
}
@Test
- public void nonGlobalAdmin_canNotDeleteTheirRelatedMembership() {
+ public void partnerRelationAgent_canNotDeleteTheirRelatedMembership() {
// given
context("superuser-alex@hostsharing.net");
- final var givenMembership = givenSomeTemporaryMembership("First", "Third", "14");
+ final var givenMembership = givenSomeTemporaryMembership("First", "14");
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent");
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent();
membershipRepo.deleteByUuid(givenMembership.getUuid());
@@ -345,11 +316,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
- final var givenMembership = givenSomeTemporaryMembership("First", "First", "15");
- assertThat(distinctRoleNamesOf(rawRoleRepo.findAll()).size()).as("precondition failed: unexpected number of roles created")
- .isEqualTo(initialRoleNames.length + 5);
- assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()).size()).as("precondition failed: unexpected number of grants created")
- .isEqualTo(initialGrantNames.length + 18);
+ final var givenMembership = givenSomeTemporaryMembership("First", "15");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -379,19 +346,18 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
- "[creating Membership test-data FirstGmbH11, hs_office_membership, INSERT]",
- "[creating Membership test-data Seconde.K.12, hs_office_membership, INSERT]");
+ "[creating Membership test-data P-10001M-...01, hs_office_membership, INSERT]",
+ "[creating Membership test-data P-10002M-...02, hs_office_membership, INSERT]",
+ "[creating Membership test-data P-10003M-...03, hs_office_membership, INSERT]");
}
- private HsOfficeMembershipEntity givenSomeTemporaryMembership(final String partnerTradeName, final String debitorName, final String memberNumberSuffix) {
+ private HsOfficeMembershipEntity givenSomeTemporaryMembership(final String partnerTradeName, final String memberNumberSuffix) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerTradeName).get(0);
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike(debitorName).get(0);
final var newMembership = HsOfficeMembershipEntity.builder()
.memberNumberSuffix(memberNumberSuffix)
.partner(givenPartner)
- .mainDebitor(givenDebitor)
.validity(Range.closedInfinite(LocalDate.parse("2020-01-01")))
.membershipFeeBillable(true)
.build();
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index c78ad519..bb42901d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -182,26 +182,26 @@ public class ImportOfficeData extends ContextBasedTest {
// no contacts yet => mostly null values
assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace("""
{
- 17=partner(null null, null),
- 20=partner(null null, null),
- 22=partner(null null, null),
- 99=partner(null null, null)
+ 17=partner(P-10017: null null, null),
+ 20=partner(P-10020: null null, null),
+ 22=partner(P-11022: null null, null),
+ 99=partner(P-19999: null null, null)
}
""");
assertThat(toFormattedString(contacts)).isEqualTo("{}");
assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace("""
{
- 17=debitor(D-1001700: null null, null: mih),
- 20=debitor(D-1002000: null null, null: xyz),
- 22=debitor(D-1102200: null null, null: xxx),
- 99=debitor(D-1999900: null null, null: zzz)
+ 17=debitor(D-1001700: rel(anchor='null null, null', type='DEBITOR'), mih),
+ 20=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz),
+ 22=debitor(D-1102200: rel(anchor='null null, null', type='DEBITOR'), xxx),
+ 99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz)
}
""");
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
- 17=Membership(M-1001700, null null, null, D-1001700, [2000-12-06,), NONE),
- 20=Membership(M-1002000, null null, null, D-1002000, [2000-12-06,2016-01-01), UNKNOWN),
- 22=Membership(M-1102200, null null, null, D-1102200, [2021-04-01,), NONE)
+ 17=Membership(M-1001700, P-10017, [2000-12-06,), NONE),
+ 20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
+ 22=Membership(M-1102200, P-11022, [2021-04-01,), NONE)
}
""");
}
@@ -226,7 +226,7 @@ public class ImportOfficeData extends ContextBasedTest {
.type(HsOfficeRelationType.DEBITOR)
.anchor(debitor.getPartner().getPartnerRel().getHolder())
.holder(debitor.getPartner().getPartnerRel().getHolder()) // just 1 debitor/partner in legacy hsadmin
- .contact(debitor.getBillingContact())
+ // FIXME .contact()
.build();
if (debitorRel.getAnchor() != null && debitorRel.getHolder() != null &&
debitorRel.getContact() != null ) {
@@ -242,10 +242,10 @@ public class ImportOfficeData extends ContextBasedTest {
assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace("""
{
- 17=partner(NP Mellies, Michael: Herr Michael Mellies ),
- 20=partner(LP JM GmbH: Herr Philip Meyer-Contract , JM GmbH),
- 22=partner(?? Test PS: Petra Schmidt , Test PS),
- 99=partner(null null, null)
+ 17=partner(P-10017: NP Mellies, Michael, Herr Michael Mellies ),
+ 20=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH),
+ 22=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS),
+ 99=partner(P-19999: null null, null)
}
""");
assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace("""
@@ -275,41 +275,46 @@ public class ImportOfficeData extends ContextBasedTest {
""");
assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace("""
{
- 17=debitor(D-1001700: NP Mellies, Michael: mih),
- 20=debitor(D-1002000: LP JM GmbH: xyz),
- 22=debitor(D-1102200: ?? Test PS: xxx),
- 99=debitor(D-1999900: null null, null: zzz)
+ 17=debitor(D-1001700: rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael'), mih),
+ 20=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz),
+ 22=debitor(D-1102200: rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS'), xxx),
+ 99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz)
}
""");
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
- 17=Membership(M-1001700, NP Mellies, Michael, D-1001700, [2000-12-06,), NONE),
- 20=Membership(M-1002000, LP JM GmbH, D-1002000, [2000-12-06,2016-01-01), UNKNOWN),
- 22=Membership(M-1102200, ?? Test PS, D-1102200, [2021-04-01,), NONE)
+ 17=Membership(M-1001700, P-10017, [2000-12-06,), NONE),
+ 20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
+ 22=Membership(M-1102200, P-11022, [2021-04-01,), NONE)
}
""");
assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace("""
{
2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000001=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000002=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000003=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
- 2000004=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000005=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000006=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
- 2000007=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000008=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000009=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
- 2000010=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000011=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000012=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
- 2000013=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
- 2000014=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000015=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
- 2000016=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
- 2000017=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
- 2000018=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
- 2000019=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS')
+ 2000001=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000002=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000003=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000004=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
+ 2000005=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
+ 2000006=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000007=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000008=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000009=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
+ 2000010=rel(anchor='null null, null', type='DEBITOR'),
+ 2000011=rel(anchor='null null, null', type='DEBITOR'),
+ 2000012=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000013=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
+ 2000014=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
+ 2000015=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000016=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000017=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
+ 2000018=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000019=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000020=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
+ 2000021=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
+ 2000022=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+ 2000023=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
+2000024=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga ')
}
""");
}
@@ -333,9 +338,9 @@ public class ImportOfficeData extends ContextBasedTest {
assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace("""
{
- 234234=bankAccount(holder='Michael Mellies', iban='DE37500105177419788228', bic='INGDDEFFXXX'),
- 235600=bankAccount(holder='JM e.K.', iban='DE02300209000106531065', bic='CMCIDEDD'),
- 235662=bankAccount(holder='JM GmbH', iban='DE49500105174516484892', bic='INGDDEFFXXX')
+ 234234=bankAccount(DE37500105177419788228: holder='Michael Mellies', bic='INGDDEFFXXX'),
+ 235600=bankAccount(DE02300209000106531065: holder='JM e.K.', bic='CMCIDEDD'),
+ 235662=bankAccount(DE49500105174516484892: holder='JM GmbH', bic='INGDDEFFXXX')
}
""");
assertThat(toFormattedString(sepaMandates)).isEqualToIgnoringWhitespace("""
@@ -359,7 +364,7 @@ public class ImportOfficeData extends ContextBasedTest {
}
@Test
- @Order(1049)
+ @Order(1041)
void verifyCoopShares() {
assumeThatWeAreImportingControlledTestData();
@@ -392,14 +397,14 @@ public class ImportOfficeData extends ContextBasedTest {
assertThat(toFormattedString(coopAssets)).isEqualToIgnoringWhitespace("""
{
- 30000=CoopAssetsTransaction(1001700, 2000-12-06, DEPOSIT, 1280.00, for subscription A),
- 31000=CoopAssetsTransaction(1002000, 2000-12-06, DEPOSIT, 128.00, for subscription B),
- 32000=CoopAssetsTransaction(1001700, 2005-01-10, DEPOSIT, 2560.00, for subscription C),
- 33001=CoopAssetsTransaction(1001700, 2005-01-10, TRANSFER, -512.00, for transfer to 10),
- 33002=CoopAssetsTransaction(1002000, 2005-01-10, ADOPTION, 512.00, for transfer from 7),
- 34001=CoopAssetsTransaction(1002000, 2016-12-31, CLEARING, -8.00, for cancellation D),
- 34002=CoopAssetsTransaction(1002000, 2016-12-31, DISBURSAL, -100.00, for cancellation D),
- 34003=CoopAssetsTransaction(1002000, 2016-12-31, LOSS, -20.00, for cancellation D)
+ 30000=CoopAssetsTransaction(M-1001700: 2000-12-06, DEPOSIT, 1280.00, for subscription A),
+ 31000=CoopAssetsTransaction(M-1002000: 2000-12-06, DEPOSIT, 128.00, for subscription B),
+ 32000=CoopAssetsTransaction(M-1001700: 2005-01-10, DEPOSIT, 2560.00, for subscription C),
+ 33001=CoopAssetsTransaction(M-1001700: 2005-01-10, TRANSFER, -512.00, for transfer to 10),
+ 33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, for transfer from 7),
+ 34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, for cancellation D),
+ 34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, for cancellation D),
+ 34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, for cancellation D)
}
""");
}
@@ -408,11 +413,13 @@ public class ImportOfficeData extends ContextBasedTest {
@Order(2000)
void verifyAllPartnersHavePersons() {
partners.forEach((id, p) -> {
+ final var partnerRel = p.getPartnerRel();
+ assertThat(partnerRel).describedAs("partner " + id + " without partnerRel").isNotNull();
if ( id != 99 ) {
- assertThat(p.getContact()).describedAs("partner " + id + " without contact").isNotNull();
- assertThat(p.getContact().getLabel()).describedAs("partner " + id + " without valid contact").isNotNull();
- assertThat(p.getPerson()).describedAs("partner " + id + " without person").isNotNull();
- assertThat(p.getPerson().getPersonType()).describedAs("partner " + id + " without valid person").isNotNull();
+ assertThat(partnerRel.getContact()).describedAs("partner " + id + " without partnerRel.contact").isNotNull();
+ assertThat(partnerRel.getContact().getLabel()).describedAs("partner " + id + " without valid partnerRel.contact").isNotNull();
+ assertThat(partnerRel.getHolder()).describedAs("partner " + id + " without partnerRel.relHolder").isNotNull();
+ assertThat(partnerRel.getHolder().getPersonType()).describedAs("partner " + id + " without valid partnerRel.relHolder").isNotNull();
}
});
}
@@ -422,17 +429,21 @@ public class ImportOfficeData extends ContextBasedTest {
void removeEmptyRelations() {
assumeThatWeAreImportingControlledTestData();
- // avoid a error when persisting the deliberetely invalid partner entry #99
+ // avoid a error when persisting the deliberately invalid partner entry #99
final var idsToRemove = new HashSet();
relations.forEach( (id, r) -> {
// such a record
if (r.getContact() == null || r.getContact().getLabel() == null ||
- r.getHolder() == null | r.getHolder().getPersonType() == null ) {
+ r.getHolder() == null || r.getHolder().getPersonType() == null ) {
idsToRemove.add(id);
}
});
- assertThat(idsToRemove.size()).isEqualTo(1); // only from partner #99 (partner+contractual roles)
- idsToRemove.forEach(id -> relations.remove(id));
+
+ // expected relations created from partner #99 + Hostsharing eG itself
+ idsToRemove.forEach(id -> {
+ System.out.println("removing unused relation: " + relations.get(id).toString());
+ relations.remove(id);
+ });
}
@Test
@@ -443,14 +454,20 @@ public class ImportOfficeData extends ContextBasedTest {
// avoid a error when persisting the deliberately invalid partner entry #99
final var idsToRemove = new HashSet();
partners.forEach( (id, r) -> {
- // such a record
- if (r.getContact() == null || r.getContact().getLabel() == null ||
- r.getPerson() == null | r.getPerson().getPersonType() == null ) {
+ final var partnerRole = r.getPartnerRel();
+
+ // such a record is in test data to test error messages
+ if (partnerRole.getContact() == null || partnerRole.getContact().getLabel() == null ||
+ partnerRole.getHolder() == null | partnerRole.getHolder().getPersonType() == null ) {
idsToRemove.add(id);
}
});
- assertThat(idsToRemove.size()).isEqualTo(1); // only from partner #99
- idsToRemove.forEach(id -> partners.remove(id));
+
+ // expected partners created from partner #99 + Hostsharing eG itself
+ idsToRemove.forEach(id -> {
+ System.out.println("removing unused partner: " + partners.get(id).toString());
+ partners.remove(id);
+ });
}
@Test
@@ -460,10 +477,11 @@ public class ImportOfficeData extends ContextBasedTest {
// avoid a error when persisting the deliberately invalid partner entry #99
final var idsToRemove = new HashSet();
- debitors.forEach( (id, r) -> {
- // such a record
- if (r.getBillingContact() == null || r.getBillingContact().getLabel() == null ||
- r.getPartner().getPerson() == null | r.getPartner().getPerson().getPersonType() == null ) {
+ debitors.forEach( (id, d) -> {
+ final var debitorRel = d.getDebitorRel();
+ if (debitorRel.getContact() == null || debitorRel.getContact().getLabel() == null ||
+ debitorRel.getAnchor() == null || debitorRel.getAnchor().getPersonType() == null ||
+ debitorRel.getHolder() == null || debitorRel.getHolder().getPersonType() == null ) {
idsToRemove.add(id);
}
});
@@ -500,13 +518,23 @@ public class ImportOfficeData extends ContextBasedTest {
jpaAttempt.transacted(() -> {
context(rbacSuperuser);
- partners.forEach(this::persist);
+ partners.forEach((id, partner) -> {
+ // TODO: this is ugly and I don't know why it's suddenly necessary
+ partner.getPartnerRel().setAnchor(em.merge(partner.getPartnerRel().getAnchor()));
+ partner.getPartnerRel().setHolder(em.merge(partner.getPartnerRel().getHolder()));
+ partner.getPartnerRel().setContact(em.merge(partner.getPartnerRel().getContact()));
+ partner.setPartnerRel(em.merge(partner.getPartnerRel()));
+ em.persist(partner);
+ });
updateLegacyIds(partners, "hs_office_partner_legacy_id", "bp_id");
}).assertSuccessful();
jpaAttempt.transacted(() -> {
context(rbacSuperuser);
- debitors.forEach(this::persist);
+ debitors.forEach((id, debitor) -> {
+ debitor.setDebitorRel(em.merge(debitor.getDebitorRel()));
+ em.persist(debitor);
+ });
}).assertSuccessful();
jpaAttempt.transacted(() -> {
@@ -676,28 +704,30 @@ public class ImportOfficeData extends ContextBasedTest {
.forEach(rec -> {
final var person = HsOfficePersonEntity.builder().build();
- final var partnerRelation = HsOfficeRelationEntity.builder()
- .holder(person)
- .type(HsOfficeRelationType.PARTNER)
- .anchor(mandant)
- .contact(null) // is set during contacts import depending on assigned roles
- .build();
- relations.put(relationId++, partnerRelation);
+ final var partnerRel = addRelation(
+ HsOfficeRelationType.PARTNER, mandant, person,
+ null // is set during contacts import depending on assigned roles
+ );
final var partner = HsOfficePartnerEntity.builder()
.partnerNumber(rec.getInteger("member_id"))
.details(HsOfficePartnerDetailsEntity.builder().build())
- .partnerRel(partnerRelation)
- .contact(null) // is set during contacts import depending on assigned roles
- .person(person)
+ .partnerRel(partnerRel)
.build();
partners.put(rec.getInteger("bp_id"), partner);
+ final var debitorRel = addRelation(
+ HsOfficeRelationType.DEBITOR, partnerRel.getHolder(), // partner person
+ null, // will be set in contacts import
+ null // will beset in contacts import
+ );
+ relations.put(relationId++, debitorRel);
+
final var debitor = HsOfficeDebitorEntity.builder()
- .partner(partner)
.debitorNumberSuffix((byte) 0)
- .defaultPrefix(rec.getString("member_code").replace("hsh00-", ""))
.partner(partner)
+ .debitorRel(debitorRel)
+ .defaultPrefix(rec.getString("member_code").replace("hsh00-", ""))
.billable(rec.isEmpty("free") || rec.getString("free").equals("f"))
.vatReverseCharge(rec.getBoolean("exempt_vat"))
.vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove
@@ -718,7 +748,6 @@ public class ImportOfficeData extends ContextBasedTest {
isBlank(rec.getString("member_until"))
? HsOfficeReasonForTermination.NONE
: HsOfficeReasonForTermination.UNKNOWN)
- .mainDebitor(debitor)
.build();
memberships.put(rec.getInteger("bp_id"), membership);
}
@@ -844,45 +873,45 @@ public class ImportOfficeData extends ContextBasedTest {
final var partner = partners.get(bpId);
final var debitor = debitors.get(bpId);
- final var partnerPerson = partner.getPerson();
+ final var partnerPerson = partner.getPartnerRel().getHolder();
if (containsPartnerRel(rec)) {
- initPerson(partner.getPerson(), rec);
+ addPerson(partnerPerson, rec);
}
HsOfficePersonEntity contactPerson = partnerPerson;
if (!StringUtils.equals(rec.getString("firma"), partnerPerson.getTradeName()) ||
!StringUtils.equals(rec.getString("first_name"), partnerPerson.getGivenName()) ||
!StringUtils.equals(rec.getString("last_name"), partnerPerson.getFamilyName())) {
- contactPerson = initPerson(HsOfficePersonEntity.builder().build(), rec);
+ contactPerson = addPerson(HsOfficePersonEntity.builder().build(), rec);
}
final var contact = HsOfficeContactEntity.builder().build();
initContact(contact, rec);
if (containsPartnerRel(rec)) {
- assertThat(partner.getContact()).isNull();
- partner.setContact(contact);
+ assertThat(partner.getPartnerRel().getContact()).isNull();
partner.getPartnerRel().setContact(contact);
}
if (containsRole(rec, "billing")) {
- assertThat(debitor.getBillingContact()).isNull();
- debitor.setBillingContact(contact);
+ assertThat(debitor.getDebitorRel().getContact()).isNull();
+ debitor.getDebitorRel().setHolder(contactPerson);
+ debitor.getDebitorRel().setContact(contact);
}
if (containsRole(rec, "operation")) {
- addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.OPERATIONS);
+ addRelation(HsOfficeRelationType.OPERATIONS, partnerPerson, contactPerson, contact);
}
if (containsRole(rec, "contractual")) {
- addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.REPRESENTATIVE);
+ addRelation(HsOfficeRelationType.REPRESENTATIVE, partnerPerson, contactPerson, contact);
}
if (containsRole(rec, "ex-partner")) {
- addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.EX_PARTNER);
+ addRelation(HsOfficeRelationType.EX_PARTNER, partnerPerson, contactPerson, contact);
}
if (containsRole(rec, "vip-contact")) {
- addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.VIP_CONTACT);
+ addRelation(HsOfficeRelationType.VIP_CONTACT, partnerPerson, contactPerson, contact);
}
for (String subscriberRole: SUBSCRIBER_ROLES) {
if (containsRole(rec, subscriberRole)) {
- addRelation(partnerPerson, contactPerson, contact, HsOfficeRelationType.SUBSCRIBER)
+ addRelation(HsOfficeRelationType.SUBSCRIBER, partnerPerson, contactPerson, contact)
.setMark(subscriberRole.split(":")[1])
;
}
@@ -896,13 +925,14 @@ public class ImportOfficeData extends ContextBasedTest {
private static void optionallyAddMissingContractualRelations() {
final var contractualMissing = new HashSet();
partners.forEach( (id, partner) -> {
- final var partnerPerson = partner.getPerson();
+ final var partnerPerson = partner.getPartnerRel().getHolder();
if (relations.values().stream()
.filter(rel -> rel.getAnchor() == partnerPerson && rel.getType() == HsOfficeRelationType.REPRESENTATIVE)
.findFirst().isEmpty()) {
contractualMissing.add(partner.getPartnerNumber());
}
});
+ assertThat(contractualMissing).containsOnly(19999); // deliberately wrong partner entry
}
private static boolean containsRole(final Record rec, final String role) {
final var roles = rec.getString("roles");
@@ -914,21 +944,21 @@ public class ImportOfficeData extends ContextBasedTest {
}
private static HsOfficeRelationEntity addRelation(
- final HsOfficePersonEntity partnerPerson,
- final HsOfficePersonEntity contactPerson,
- final HsOfficeContactEntity contact,
- final HsOfficeRelationType representative) {
+ final HsOfficeRelationType type,
+ final HsOfficePersonEntity anchor,
+ final HsOfficePersonEntity holder,
+ final HsOfficeContactEntity contact) {
final var rel = HsOfficeRelationEntity.builder()
- .anchor(partnerPerson)
- .holder(contactPerson)
+ .anchor(anchor)
+ .holder(holder)
.contact(contact)
- .type(representative)
+ .type(type)
.build();
relations.put(relationId++, rel);
return rel;
}
- private HsOfficePersonEntity initPerson(final HsOfficePersonEntity person, final Record contactRecord) {
+ private HsOfficePersonEntity addPerson(final HsOfficePersonEntity person, final Record contactRecord) {
// TODO: title+salutation: add to person
person.setGivenName(contactRecord.getString("first_name"));
person.setFamilyName(contactRecord.getString("last_name"));
@@ -1141,11 +1171,6 @@ class Record {
return value == null || value.isBlank();
}
- Byte getByte(final String columnName) {
- final String value = getString(columnName);
- return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0;
- }
-
boolean getBoolean(final String columnName) {
final String value = getString(columnName);
return isNotBlank(value) &&
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
index 9e712a2d..e8eac1c1 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java
@@ -91,9 +91,9 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
void globalAdmin_withoutAssumedRole_canAddPartner() {
context.define("superuser-alex@hostsharing.net");
- final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
- final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
+ final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").stream().findFirst().orElseThrow();
+ final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").stream().findFirst().orElseThrow();
+ final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").stream().findFirst().orElseThrow();
final var location = RestAssured // @formatter:off
.given()
@@ -107,8 +107,6 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
"holderUuid": "%s",
"contactUuid": "%s"
},
- "personUuid": "%s",
- "contactUuid": "%s",
"details": {
"registrationOffice": "Temp Registergericht Aurich",
"registrationNumber": "111111"
@@ -117,21 +115,29 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
""".formatted(
givenMandantPerson.getUuid(),
givenPerson.getUuid(),
- givenContact.getUuid(),
- givenPerson.getUuid(),
givenContact.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/partners")
- .then().assertThat()
+ .then().log().body().assertThat()
.statusCode(201)
.contentType(ContentType.JSON)
- .body("uuid", isUuidValid())
- .body("partnerNumber", is(20002))
- .body("details.registrationOffice", is("Temp Registergericht Aurich"))
- .body("details.registrationNumber", is("111111"))
- .body("contact.label", is(givenContact.getLabel()))
- .body("person.tradeName", is(givenPerson.getTradeName()))
+ .body("", lenientlyEquals("""
+ {
+ "partnerNumber": 20002,
+ "partnerRel": {
+ "anchor": { "tradeName": "Hostsharing eG" },
+ "holder": { "tradeName": "Third OHG" },
+ "type": "PARTNER",
+ "mark": null,
+ "contact": { "label": "fourth contact" }
+ },
+ "details": {
+ "registrationOffice": "Temp Registergericht Aurich",
+ "registrationNumber": "111111"
+ }
+ }
+ """))
.header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on
@@ -226,6 +232,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
@Test
void globalAdmin_withoutAssumedRole_canGetArbitraryPartner() {
context.define("superuser-alex@hostsharing.net");
+ final var partners = partnerRepo.findAll();
final var givenPartnerUuid = partnerRepo.findPartnerByOptionalNameLike("First").get(0).getUuid();
RestAssured // @formatter:off
@@ -239,8 +246,18 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "person": { "tradeName": "First GmbH" },
- "contact": { "label": "first contact" }
+ "partnerNumber": 10001,
+ "partnerRel": {
+ "anchor": { "tradeName": "Hostsharing eG" },
+ "holder": { "tradeName": "First GmbH" },
+ "type": "PARTNER",
+ "contact": { "label": "first contact" }
+ },
+ "details": {
+ "registrationOffice": "Hamburg",
+ "registrationNumber": "RegNo123456789"
+ }
+ }
}
""")); // @formatter:on
}
@@ -278,8 +295,10 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "person": { "tradeName": "First GmbH" },
- "contact": { "label": "first contact" }
+ "partnerRel": {
+ "holder": { "tradeName": "First GmbH" },
+ "contact": { "label": "first contact" }
+ }
}
""")); // @formatter:on
}
@@ -295,8 +314,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
context.define("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(20011);
- final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
+ final var givenPartnerRel = givenSomeTemporaryPartnerRel("Third OHG", "third contact");
RestAssured // @formatter:off
.given()
@@ -305,8 +323,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("""
{
"partnerNumber": "20011",
- "contactUuid": "%s",
- "personUuid": "%s",
+ "partnerRelUuid": "%s",
"details": {
"registrationOffice": "Temp Registergericht Aurich",
"registrationNumber": "222222",
@@ -315,18 +332,32 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
"dateOfDeath": "2022-01-12"
}
}
- """.formatted(givenContact.getUuid(), givenPerson.getUuid()))
+ """.formatted(givenPartnerRel.getUuid()))
.port(port)
.when()
.patch("http://localhost/api/hs/office/partners/" + givenPartner.getUuid())
- .then().assertThat()
+ .then().log().body().assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
- .body("uuid", is(givenPartner.getUuid().toString())) // not patched!
- .body("partnerNumber", is(givenPartner.getPartnerNumber())) // not patched!
- .body("details.registrationNumber", is("222222"))
- .body("contact.label", is(givenContact.getLabel()))
- .body("person.tradeName", is(givenPerson.getTradeName()));
+ .body("", lenientlyEquals("""
+ {
+ "partnerNumber": 20011,
+ "partnerRel": {
+ "anchor": { "tradeName": "Hostsharing eG" },
+ "holder": { "tradeName": "Third OHG" },
+ "type": "PARTNER",
+ "contact": { "label": "third contact" }
+ },
+ "details": {
+ "registrationOffice": "Temp Registergericht Aurich",
+ "registrationNumber": "222222",
+ "birthName": "Maja Schmidt",
+ "birthPlace": null,
+ "birthday": "1938-04-08",
+ "dateOfDeath": "2022-01-12"
+ }
+ }
+ """));
// @formatter:on
// finally, the partner is actually updated
@@ -334,8 +365,8 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isPresent().get()
.matches(partner -> {
assertThat(partner.getPartnerNumber()).isEqualTo(givenPartner.getPartnerNumber());
- assertThat(partner.getPerson().getTradeName()).isEqualTo("Third OHG");
- assertThat(partner.getContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(partner.getPartnerRel().getHolder().getTradeName()).isEqualTo("Third OHG");
+ assertThat(partner.getPartnerRel().getContact().getLabel()).isEqualTo("third contact");
assertThat(partner.getDetails().getRegistrationOffice()).isEqualTo("Temp Registergericht Aurich");
assertThat(partner.getDetails().getRegistrationNumber()).isEqualTo("222222");
assertThat(partner.getDetails().getBirthName()).isEqualTo("Maja Schmidt");
@@ -371,16 +402,18 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("details.birthName", is("Maja Schmidt"))
- .body("contact.label", is(givenPartner.getContact().getLabel()))
- .body("person.tradeName", is(givenPartner.getPerson().getTradeName()));
+ .body("details.birthName", is("Maja Schmidt"));
+ // TODO: assert partnerRel
+// .body("contact.label", is(givenPartner.getContact().getLabel()))
+// .body("person.tradeName", is(givenPartner.getPerson().getTradeName()));
// @formatter:on
// finally, the partner is actually updated
assertThat(partnerRepo.findByUuid(givenPartner.getUuid())).isPresent().get()
.matches(person -> {
- assertThat(person.getPerson().getTradeName()).isEqualTo(givenPartner.getPerson().getTradeName());
- assertThat(person.getContact().getLabel()).isEqualTo(givenPartner.getContact().getLabel());
+ // TODO: assert partnerRel
+// assertThat(person.getPerson().getTradeName()).isEqualTo(givenPartner.getPerson().getTradeName());
+// assertThat(person.getContact().getLabel()).isEqualTo(givenPartner.getContact().getLabel());
assertThat(person.getDetails().getRegistrationOffice()).isEqualTo("Temp Registergericht Leer");
assertThat(person.getDetails().getRegistrationNumber()).isEqualTo("333333");
assertThat(person.getDetails().getBirthName()).isEqualTo("Maja Schmidt");
@@ -421,7 +454,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
void contactAdminUser_canNotDeleteRelatedPartner() {
context.define("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(20014);
- assertThat(givenPartner.getContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(givenPartner.getPartnerRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off
.given()
@@ -441,7 +474,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
void normalUser_canNotDeleteUnrelatedPartner() {
context.define("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(20015);
- assertThat(givenPartner.getContact().getLabel()).isEqualTo("fourth contact");
+ assertThat(givenPartner.getPartnerRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off
.given()
@@ -457,13 +490,14 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
}
}
- private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final Integer partnerNumber) {
+ private HsOfficeRelationEntity givenSomeTemporaryPartnerRel(
+ final String partnerHolderName,
+ final String contactName) {
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
- final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
-
- final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
+ final var givenMandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").stream().findFirst().orElseThrow();
+ final var givenPerson = personRepo.findPersonByOptionalNameLike(partnerHolderName).stream().findFirst().orElseThrow();
+ final var givenContact = contactRepo.findContactByOptionalLabelLike(contactName).stream().findFirst().orElseThrow();
final var partnerRel = new HsOfficeRelationEntity();
partnerRel.setType(HsOfficeRelationType.PARTNER);
@@ -471,12 +505,17 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
partnerRel.setHolder(givenPerson);
partnerRel.setContact(givenContact);
em.persist(partnerRel);
+ return partnerRel;
+ }).assertSuccessful().returnedValue();
+ }
+ private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final Integer partnerNumber) {
+ return jpaAttempt.transacted(() -> {
+ context.define("superuser-alex@hostsharing.net");
+ final var partnerRel = em.merge(givenSomeTemporaryPartnerRel("Erben Bessler", "fourth contact"));
final var newPartner = HsOfficePartnerEntity.builder()
.partnerRel(partnerRel)
.partnerNumber(partnerNumber)
- .person(givenPerson)
- .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder()
.registrationOffice("Temp Registergericht Leer")
.registrationNumber("333333")
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
index e86cbc94..e6e7fb7e 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerRestTest.java
@@ -191,31 +191,5 @@ class HsOfficePartnerControllerRestTest {
// then
.andExpect(status().isForbidden());
}
-
- @Test
- void respondBadRequest_ifRelationCannotBeDeleted() throws Exception {
- // given
- final UUID givenPartnerUuid = UUID.randomUUID();
- when(partnerRepo.findByUuid(givenPartnerUuid)).thenReturn(Optional.of(partnerMock));
- when(partnerRepo.deleteByUuid(givenPartnerUuid)).thenReturn(1);
- when(relationRepo.deleteByUuid(any())).thenReturn(0);
-
- final UUID givenRelationUuid = UUID.randomUUID();
- when(partnerMock.getPartnerRel()).thenReturn(HsOfficeRelationEntity.builder()
- .uuid(givenRelationUuid)
- .build());
- when(relationRepo.deleteByUuid(givenRelationUuid)).thenReturn(0);
-
- // when
- mockMvc.perform(MockMvcRequestBuilders
- .delete("/api/hs/office/partners/" + givenPartnerUuid)
- .header("current-user", "superuser-alex@hostsharing.net")
- .contentType(MediaType.APPLICATION_JSON)
- .accept(MediaType.APPLICATION_JSON))
-
- // then
- .andExpect(status().isForbidden());
- }
-
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcherUnitTest.java
index 5fe483ae..7f350649 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityPatcherUnitTest.java
@@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance;
@@ -30,8 +31,7 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID();
private static final UUID INITIAL_PERSON_UUID = UUID.randomUUID();
private static final UUID INITIAL_DETAILS_UUID = UUID.randomUUID();
- private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
- private static final UUID PATCHED_PERSON_UUID = UUID.randomUUID();
+ private static final UUID PATCHED_PARTNER_ROLE_UUID = UUID.randomUUID();
private final HsOfficePersonEntity givenInitialPerson = HsOfficePersonEntity.builder()
.uuid(INITIAL_PERSON_UUID)
@@ -48,19 +48,21 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
@BeforeEach
void initMocks() {
- lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation ->
- HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build());
- lenient().when(em.getReference(eq(HsOfficePersonEntity.class), any())).thenAnswer(invocation ->
- HsOfficePersonEntity.builder().uuid(invocation.getArgument(1)).build());
+ lenient().when(em.getReference(eq(HsOfficeRelationEntity.class), any())).thenAnswer(invocation ->
+ HsOfficeRelationEntity.builder().uuid(invocation.getArgument(1)).build());
}
@Override
protected HsOfficePartnerEntity newInitialEntity() {
- final var entity = new HsOfficePartnerEntity();
- entity.setUuid(INITIAL_PARTNER_UUID);
- entity.setPerson(givenInitialPerson);
- entity.setContact(givenInitialContact);
- entity.setDetails(givenInitialDetails);
+ final var entity = HsOfficePartnerEntity.builder()
+ .uuid(INITIAL_PARTNER_UUID)
+ .partnerNumber(12345)
+ .partnerRel(HsOfficeRelationEntity.builder()
+ .holder(givenInitialPerson)
+ .contact(givenInitialContact)
+ .build())
+ .details(givenInitialDetails)
+ .build();
return entity;
}
@@ -78,31 +80,19 @@ class HsOfficePartnerEntityPatcherUnitTest extends PatchUnitTestBase<
protected Stream propertyTestDescriptors() {
return Stream.of(
new JsonNullableProperty<>(
- "contact",
- HsOfficePartnerPatchResource::setContactUuid,
- PATCHED_CONTACT_UUID,
- HsOfficePartnerEntity::setContact,
- newContact(PATCHED_CONTACT_UUID))
- .notNullable(),
- new JsonNullableProperty<>(
- "person",
- HsOfficePartnerPatchResource::setPersonUuid,
- PATCHED_PERSON_UUID,
- HsOfficePartnerEntity::setPerson,
- newPerson(PATCHED_PERSON_UUID))
+ "partnerRel",
+ HsOfficePartnerPatchResource::setPartnerRelUuid,
+ PATCHED_PARTNER_ROLE_UUID,
+ HsOfficePartnerEntity::setPartnerRel,
+ newPartnerRel(PATCHED_PARTNER_ROLE_UUID))
.notNullable()
);
}
- private static HsOfficeContactEntity newContact(final UUID uuid) {
- final var newContact = new HsOfficeContactEntity();
- newContact.setUuid(uuid);
- return newContact;
- }
-
- private HsOfficePersonEntity newPerson(final UUID uuid) {
- final var newPerson = new HsOfficePersonEntity();
- newPerson.setUuid(uuid);
- return newPerson;
+ private static HsOfficeRelationEntity newPartnerRel(final UUID uuid) {
+ final var newPartnerRel = HsOfficeRelationEntity.builder()
+ .uuid(uuid)
+ .build();
+ return newPartnerRel;
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java
index a6d2c60a..62d81416 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java
@@ -3,39 +3,39 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class HsOfficePartnerEntityUnitTest {
+ private final HsOfficePartnerEntity givenPartner = HsOfficePartnerEntity.builder()
+ .partnerNumber(12345)
+ .partnerRel(HsOfficeRelationEntity.builder()
+ .anchor(HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.LEGAL_PERSON)
+ .tradeName("Hostsharing eG")
+ .build())
+ .type(HsOfficeRelationType.PARTNER)
+ .holder(HsOfficePersonEntity.builder()
+ .personType(HsOfficePersonType.LEGAL_PERSON)
+ .tradeName("some trade name")
+ .build())
+ .contact(HsOfficeContactEntity.builder().label("some label").build())
+ .build())
+ .build();
+
@Test
- void toStringContainsPersonAndContact() {
- final var given = HsOfficePartnerEntity.builder()
- .person(HsOfficePersonEntity.builder()
- .personType(HsOfficePersonType.LEGAL_PERSON)
- .tradeName("some trade name")
- .build())
- .contact(HsOfficeContactEntity.builder().label("some label").build())
- .build();
-
- final var result = given.toString();
-
- assertThat(result).isEqualTo("partner(LP some trade name: some label)");
+ void toStringContainsPartnerNumberPersonAndContact() {
+ final var result = givenPartner.toString();
+ assertThat(result).isEqualTo("partner(P-12345: LP some trade name, some label)");
}
@Test
- void toShortStringContainsPersonAndContact() {
- final var given = HsOfficePartnerEntity.builder()
- .person(HsOfficePersonEntity.builder()
- .personType(HsOfficePersonType.LEGAL_PERSON)
- .tradeName("some trade name")
- .build())
- .contact(HsOfficeContactEntity.builder().label("some label").build())
- .build();
-
- final var result = given.toShortString();
-
- assertThat(result).isEqualTo("LP some trade name");
+ void toShortStringContainsPartnerNumber() {
+ final var result = givenPartner.toShortString();
+ assertThat(result).isEqualTo("P-12345");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
index 75eaac3e..94bcb9fe 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
@@ -8,11 +8,11 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
+import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,9 +27,9 @@ import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
+import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacObjectEntity.objectDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.Array.fromFormatted;
import static net.hostsharing.test.JpaAttempt.attempt;
@@ -51,6 +51,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired
HsOfficeContactRepository contactRepo;
+ @Autowired
+ RawRbacObjectRepository rawObjectRepo;
+
@Autowired
RawRbacRoleRepository rawRoleRepo;
@@ -66,8 +69,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
@MockBean
HttpServletRequest request;
- Set tempPartners = new HashSet<>();
-
@Nested
class CreatePartner {
@@ -76,27 +77,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// given
context("superuser-alex@hostsharing.net");
final var count = partnerRepo.count();
- final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
- final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
-
- final var partnerRel = HsOfficeRelationEntity.builder()
- .holder(givenPartnerPerson)
- .type(HsOfficeRelationType.PARTNER)
- .anchor(givenMandantorPerson)
- .contact(givenContact)
- .build();
- relationRepo.save(partnerRel);
+ final var partnerRel = givenSomeTemporaryHostsharingPartnerRel("First GmbH", "first contact");
// when
final var result = attempt(em, () -> {
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20031)
.partnerRel(partnerRel)
- .person(givenPartnerPerson)
- .contact(givenContact)
- .details(HsOfficePartnerDetailsEntity.builder()
- .build())
+ .details(HsOfficePartnerDetailsEntity.builder().build())
.build();
return partnerRepo.save(newPartner);
});
@@ -136,8 +124,6 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(20032)
.partnerRel(newRelation)
- .person(givenPartnerPerson)
- .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build())
.build();
return partnerRepo.save(newPartner);
@@ -146,67 +132,52 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
"hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner",
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant",
- "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.admin",
- "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.agent",
- "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.owner",
- "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.tenant",
- "hs_office_partner#20032:ErbenBesslerMelBessler-fourthcontact.guest"));
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.agent",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("fourthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(fromFormatted(
initialGrantNames,
- // relation - TODO: check and cleanup
- "{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }",
- "{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role partner#20032:EBess-4th.tenant by system and assume }",
- "{ grant role partner#20032:EBess-4th.agent to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm INSERT into sepamandate with relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+
+ // permissions on partner
+ "{ grant perm DELETE on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm UPDATE on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
+ "{ grant perm SELECT on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+
+ // permissions on partner-details
+ "{ grant perm DELETE on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm UPDATE on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
+ "{ grant perm SELECT on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
+
+ // permissions on partner-relation
+ "{ grant perm DELETE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
+ "{ grant perm UPDATE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm SELECT on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+
+ // relation owner
"{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to user superuser-alex@hostsharing.net by relation#HostsharingeG-with-PARTNER-EBess.owner and assume }",
+
+ // relation admin
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role person#HostsharingeG.admin by system and assume }",
+
+ // relation agent
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.agent to role person#EBess.admin by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.agent to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+
+ // relation tenant
+ "{ grant role contact#4th.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role person#EBess.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant role person#HostsharingeG.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
"{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
"{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
- "{ grant perm UPDATE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm DELETE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant perm SELECT on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role contact#4th.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#HostsharingeG.tenant to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
-
- // owner
- "{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.owner by system and assume }",
- "{ grant perm DELETE on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.owner by system and assume }",
- "{ grant role partner#20032:EBess-4th.owner to role global#global.admin by system and assume }",
-
- // admin
- "{ grant perm UPDATE on partner#20032:EBess-4th to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant perm UPDATE on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant role partner#20032:EBess-4th.admin to role partner#20032:EBess-4th.owner by system and assume }",
- "{ grant role person#EBess.tenant to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant role contact#4th.tenant to role partner#20032:EBess-4th.admin by system and assume }",
-
- // agent
- "{ grant perm SELECT on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.agent by system and assume }",
- "{ grant role partner#20032:EBess-4th.agent to role partner#20032:EBess-4th.admin by system and assume }",
- "{ grant role partner#20032:EBess-4th.agent to role person#EBess.admin by system and assume }",
- "{ grant role partner#20032:EBess-4th.agent to role contact#4th.admin by system and assume }",
-
- // tenant
- "{ grant role partner#20032:EBess-4th.tenant to role partner#20032:EBess-4th.agent by system and assume }",
- "{ grant role person#EBess.guest to role partner#20032:EBess-4th.tenant by system and assume }",
- "{ grant role contact#4th.guest to role partner#20032:EBess-4th.tenant by system and assume }",
-
- // guest
- "{ grant perm SELECT on partner#20032:EBess-4th to role partner#20032:EBess-4th.guest by system and assume }",
- "{ grant role partner#20032:EBess-4th.guest to role partner#20032:EBess-4th.tenant by system and assume }",
-
+ "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
null)));
}
@@ -230,9 +201,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
allThesePartnersAreReturned(
result,
- "partner(IF Third OHG: third contact)",
- "partner(LP Second e.K.: second contact)",
- "partner(LP First GmbH: first contact)");
+ "partner(P-10001: LP First GmbH, first contact)",
+ "partner(P-10002: LP Second e.K., second contact)",
+ "partner(P-10003: IF Third OHG, third contact)",
+ "partner(P-10004: LP Fourth eG, fourth contact)",
+ "partner(P-10010: NP Smith, Peter, sixth contact)");
}
@Test
@@ -244,7 +217,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var result = partnerRepo.findPartnerByOptionalNameLike(null);
// then:
- exactlyThesePartnersAreReturned(result, "partner(LP First GmbH: first contact)");
+ exactlyThesePartnersAreReturned(result, "partner(P-10001: LP First GmbH, first contact)");
}
}
@@ -260,7 +233,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var result = partnerRepo.findPartnerByOptionalNameLike("third contact");
// then
- exactlyThesePartnersAreReturned(result, "partner(IF Third OHG: third contact)");
+ exactlyThesePartnersAreReturned(result, "partner(P-10003: IF Third OHG, third contact)");
}
}
@@ -279,7 +252,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThat(result)
.isNotNull()
.extracting(Object::toString)
- .isEqualTo("partner(LP First GmbH: first contact)");
+ .isEqualTo("partner(P-10001: LP First GmbH, first contact)");
}
}
@@ -290,62 +263,81 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() {
// given
context("superuser-alex@hostsharing.net");
- final var givenPartner = givenSomeTemporaryPartnerBessler(20036, "Erben Bessler", "fifth contact");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20036, "Erben Bessler", "fifth contact");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_partner#20036:ErbenBesslerMelBessler-fifthcontact.admin");
+ "hs_office_person#ErbenBesslerMelBessler.admin");
assertThatPartnerActuallyInDatabase(givenPartner);
- final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0);
- final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- givenPartner.setContact(givenNewContact);
- givenPartner.setPerson(givenNewPerson);
+ givenPartner.setPartnerRel(givenSomeTemporaryHostsharingPartnerRel("Third OHG", "sixth contact"));
return partnerRepo.save(givenPartner);
});
// then
result.assertSuccessful();
+
assertThatPartnerIsVisibleForUserWithRole(
- result.returnedValue(),
+ givenPartner,
"global#global.admin");
assertThatPartnerIsVisibleForUserWithRole(
- result.returnedValue(),
+ givenPartner,
"hs_office_person#ThirdOHG.admin");
assertThatPartnerIsNotVisibleForUserWithRole(
- result.returnedValue(),
+ givenPartner,
"hs_office_person#ErbenBesslerMelBessler.admin");
}
@Test
- @Disabled // TODO: enable once partner.person and partner.contact are removed
- public void partnerAgent_canNotUpdateRelatedPartner() {
+ public void partnerRelationAgent_canUpdateRelatedPartner() {
// given
context("superuser-alex@hostsharing.net");
- final var givenPartner = givenSomeTemporaryPartnerBessler(20037, "Erben Bessler", "ninth");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_partner#20033:ErbenBesslerMelBessler-ninthcontact.agent");
+ "hs_office_person#ErbenBesslerMelBessler.admin");
assertThatPartnerActuallyInDatabase(givenPartner);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net",
- "hs_office_partner#20033:ErbenBesslerMelBessler-ninthcontact.agent");
+ "hs_office_person#ErbenBesslerMelBessler.admin");
+ givenPartner.getDetails().setBirthName("new birthname");
+ return partnerRepo.save(givenPartner);
+ });
+
+ // then
+ result.assertSuccessful();
+ }
+
+ @Test
+ public void partnerRelationTenant_canNotUpdateRelatedPartner() {
+ // given
+ context("superuser-alex@hostsharing.net");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
+ assertThatPartnerIsVisibleForUserWithRole(
+ givenPartner,
+ "hs_office_person#ErbenBesslerMelBessler.admin");
+ assertThatPartnerActuallyInDatabase(givenPartner);
+
+ // when
+ final var result = jpaAttempt.transacted(() -> {
+ context("superuser-alex@hostsharing.net",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant");
givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner);
});
// then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] Subject ", " is not allowed to update hs_office_partner_details uuid");
+ "[403] insert into hs_office_partner_details not allowed for current subjects {hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant}");
}
private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) {
final var found = partnerRepo.findByUuid(saved.getUuid());
- assertThat(found).isNotEmpty().get().isNotSameAs(saved).usingRecursiveComparison().isEqualTo(saved);
+ assertThat(found).isNotEmpty().get().isNotSameAs(saved).extracting(HsOfficePartnerEntity::toString).isEqualTo(saved.toString());
}
private void assertThatPartnerIsVisibleForUserWithRole(
@@ -375,7 +367,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenPartner = givenSomeTemporaryPartnerBessler(20032, "Erben Bessler", "tenth");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20032, "Erben Bessler", "tenth");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -395,7 +387,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenPartner = givenSomeTemporaryPartnerBessler(20032, "Erben Bessler", "eleventh");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20033, "Erben Bessler", "eleventh");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -419,22 +411,21 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
public void deletingAPartnerAlsoDeletesRelatedRolesAndGrants() {
// given
context("superuser-alex@hostsharing.net");
+ final var initialObjects = Array.from(objectDisplaysOf(rawObjectRepo.findAll()));
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
- final var givenPartner = givenSomeTemporaryPartnerBessler(20034, "Erben Bessler", "twelfth");
+ final var givenPartner = givenSomeTemporaryHostsharingPartner(20034, "Erben Bessler", "twelfth");
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- // TODO: should deleting a partner automatically delete the PARTNER relation? (same for debitor)
- // TODO: why did the test cleanup check does not notice this, if missing?
- return partnerRepo.deleteByUuid(givenPartner.getUuid()) +
- relationRepo.deleteByUuid(givenPartner.getPartnerRel().getUuid());
+ return partnerRepo.deleteByUuid(givenPartner.getUuid());
});
// then
result.assertSuccessful();
- assertThat(result.returnedValue()).isEqualTo(2); // partner+relation
+ assertThat(result.returnedValue()).isEqualTo(1);
+ assertThat(objectDisplaysOf(rawObjectRepo.findAll())).containsExactlyInAnyOrder(initialObjects);
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(initialRoleNames);
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(initialGrantNames);
}
@@ -458,27 +449,15 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
"[creating partner test-data Seconde.K.-secondcontact, hs_office_partner, INSERT]");
}
- private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(
+ private HsOfficePartnerEntity givenSomeTemporaryHostsharingPartner(
final Integer partnerNumber, final String person, final String contact) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
- final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
- final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
-
- final var partnerRel = HsOfficeRelationEntity.builder()
- .holder(givenPartnerPerson)
- .type(HsOfficeRelationType.PARTNER)
- .anchor(givenMandantorPerson)
- .contact(givenContact)
- .build();
- relationRepo.save(partnerRel);
+ final var partnerRel = givenSomeTemporaryHostsharingPartnerRel(person, contact);
final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber)
.partnerRel(partnerRel)
- .person(givenPartnerPerson)
- .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build())
.build();
@@ -486,6 +465,21 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
}).assertSuccessful().returnedValue();
}
+ private HsOfficeRelationEntity givenSomeTemporaryHostsharingPartnerRel(final String person, final String contact) {
+ final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
+ final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
+ final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
+
+ final var partnerRel = HsOfficeRelationEntity.builder()
+ .holder(givenPartnerPerson)
+ .type(HsOfficeRelationType.PARTNER)
+ .anchor(givenMandantorPerson)
+ .contact(givenContact)
+ .build();
+ relationRepo.save(partnerRel);
+ return partnerRel;
+ }
+
void exactlyThesePartnersAreReturned(final List actualResult, final String... partnerNames) {
assertThat(actualResult)
.extracting(partnerEntity -> partnerEntity.toString())
@@ -500,9 +494,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
@AfterEach
void cleanup() {
- cleanupAllNew(HsOfficePartnerDetailsEntity.class); // TODO: should not be necessary
cleanupAllNew(HsOfficePartnerEntity.class);
- cleanupAllNew(HsOfficeRelationEntity.class);
}
private String[] distinct(final String[] strings) {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java
index abbb8e09..ce1986b2 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java
@@ -2,7 +2,8 @@ package net.hostsharing.hsadminng.hs.office.partner;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
+import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGAL_PERSON;
@@ -13,13 +14,22 @@ public class TestHsOfficePartner {
static public HsOfficePartnerEntity hsOfficePartnerWithLegalPerson(final String tradeName) {
return HsOfficePartnerEntity.builder()
.partnerNumber(10001)
- .person(HsOfficePersonEntity.builder()
- .personType(LEGAL_PERSON)
- .tradeName(tradeName)
- .build())
- .contact(HsOfficeContactEntity.builder()
- .label(tradeName)
- .build())
+ .partnerRel(
+ HsOfficeRelationEntity.builder()
+ .holder(HsOfficePersonEntity.builder()
+ .personType(LEGAL_PERSON)
+ .tradeName("Hostsharing eG")
+ .build())
+ .type(HsOfficeRelationType.PARTNER)
+ .holder(HsOfficePersonEntity.builder()
+ .personType(LEGAL_PERSON)
+ .tradeName(tradeName)
+ .build())
+ .contact(HsOfficeContactEntity.builder()
+ .label(tradeName)
+ .build())
+ .build()
+ )
.build();
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java
index 78b9c290..072df1a7 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonControllerAcceptanceTest.java
@@ -65,7 +65,7 @@ class HsOfficePersonControllerAcceptanceTest extends ContextBasedTestWithCleanup
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
- .body("", hasSize(12));
+ .body("", hasSize(13));
// @formatter:on
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
index d3da9ada..de198b47 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
@@ -59,7 +59,6 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
final var count = personRepo.count();
// when
-
final var result = attempt(em, () -> toCleanup(personRepo.save(
hsOfficePerson("a new person"))));
@@ -91,14 +90,13 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
public void createsAndGrantsRoles() {
// given
context("selfregistered-user-drew@hostsharing.org");
- final var count = personRepo.count();
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll());
// when
- attempt(em, () -> toCleanup(personRepo.save(
- hsOfficePerson("another new person")))
- ).assumeSuccessful();
+ attempt(em, () -> toCleanup(
+ personRepo.save(hsOfficePerson("another new person"))
+ )).assumeSuccessful();
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(
@@ -106,20 +104,21 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
initialRoleNames,
"hs_office_person#anothernewperson.owner",
"hs_office_person#anothernewperson.admin",
- "hs_office_person#anothernewperson.tenant",
- "hs_office_person#anothernewperson.guest"
+ "hs_office_person#anothernewperson.referrer"
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(
Array.from(
initialGrantNames,
+ "{ grant perm INSERT into hs_office_relation with hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
+
+ "{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by hs_office_person#anothernewperson.owner and assume }",
"{ grant role hs_office_person#anothernewperson.owner to role global#global.admin by system and assume }",
"{ grant perm UPDATE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
- "{ grant role hs_office_person#anothernewperson.tenant to role hs_office_person#anothernewperson.admin by system and assume }",
"{ grant perm DELETE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
"{ grant role hs_office_person#anothernewperson.admin to role hs_office_person#anothernewperson.owner by system and assume }",
- "{ grant perm SELECT on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.guest by system and assume }",
- "{ grant role hs_office_person#anothernewperson.guest to role hs_office_person#anothernewperson.tenant by system and assume }",
- "{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
+
+ "{ grant perm SELECT on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.referrer by system and assume }",
+ "{ grant role hs_office_person#anothernewperson.referrer to role hs_office_person#anothernewperson.admin by system and assume }"
));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
index c4654bd3..78d64e6a 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationControllerAcceptanceTest.java
@@ -87,7 +87,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
},
{
"anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG" },
- "holder": { "personType": "INCORPORATED_FIRM", "tradeName": "Fourth eG" },
+ "holder": { "personType": "LEGAL_PERSON", "tradeName": "Fourth eG" },
"type": "PARTNER",
"contact": { "label": "fourth contact" }
},
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
index 5c10af88..58ad8ae7 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
@@ -22,6 +22,8 @@ import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
+import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.NATURAL_PERSON;
+import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.UNINCORPORATED_FIRM;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.JpaAttempt.attempt;
@@ -63,9 +65,14 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// given
context("superuser-alex@hostsharing.net");
final var count = relationRepo.count();
- final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
- final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
+ final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
+ .filter(p -> p.getPersonType() == UNINCORPORATED_FIRM)
+ .findFirst().orElseThrow();
+ final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Paul").stream()
+ .filter(p -> p.getPersonType() == NATURAL_PERSON)
+ .findFirst().orElseThrow();
+ final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").stream()
+ .findFirst().orElseThrow();
// when
final var result = attempt(em, () -> {
@@ -86,7 +93,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
assertThat(relationRepo.count()).isEqualTo(count + 1);
final var stored = relationRepo.findByUuid(result.returnedValue().getUuid());
assertThat(stored).isNotEmpty().map(HsOfficeRelationEntity::toString).get()
- .isEqualTo("rel(anchor='NP Bessler, Anita', type='SUBSCRIBER', mark='operations-announce', holder='NP Bessler, Anita', contact='fourth contact')");
+ .isEqualTo("rel(anchor='UF Erben Bessler', type='SUBSCRIBER', mark='operations-announce', holder='NP Winkler, Paul', contact='fourth contact')");
}
@Test
@@ -98,9 +105,14 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// when
attempt(em, () -> {
- final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").get(0);
- final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Anita").get(0);
- final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
+ final var givenAnchorPerson = personRepo.findPersonByOptionalNameLike("Bessler").stream()
+ .filter(p -> p.getPersonType() == UNINCORPORATED_FIRM)
+ .findFirst().orElseThrow();
+ final var givenHolderPerson = personRepo.findPersonByOptionalNameLike("Bert").stream()
+ .filter(p -> p.getPersonType() == NATURAL_PERSON)
+ .findFirst().orElseThrow();
+ final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").stream()
+ .findFirst().orElseThrow();
final var newRelation = HsOfficeRelationEntity.builder()
.anchor(givenAnchorPerson)
.holder(givenHolderPerson)
@@ -113,26 +125,36 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin",
- "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner",
- "hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant"));
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
+ // TODO: this grant should only be created for DEBITOR-Relationships, thus the RBAC DSL needs to support conditional grants
+ "{ grant perm INSERT into hs_office_sepamandate with hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
- "{ grant perm DELETE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }",
+ "{ grant perm DELETE on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to role global#global.admin by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to user superuser-alex@hostsharing.net by hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner and assume }",
- "{ grant perm UPDATE on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }",
+ "{ grant perm UPDATE on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_person#ErbenBesslerMelBessler.admin by system and assume }",
- "{ grant perm SELECT on hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_person#BesslerBert.admin by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
+
+ "{ grant perm SELECT on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent by system and assume }",
+ "{ grant role hs_office_person#BesslerBert.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
+ "{ grant role hs_office_person#ErbenBesslerMelBessler.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
+ "{ grant role hs_office_contact#fourthcontact.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
+
+ // REPRESENTATIVE holder person -> (represented) anchor person
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
+ "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_person#BesslerBert.admin by system and assume }",
- "{ grant role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }",
- "{ grant role hs_office_contact#fourthcontact.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
- "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relation#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }",
null)
);
}
@@ -150,7 +172,9 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
public void globalAdmin_withoutAssumedRole_canViewAllRelationsOfArbitraryPerson() {
// given
context("superuser-alex@hostsharing.net");
- final var person = personRepo.findPersonByOptionalNameLike("Second e.K.").stream().findFirst().orElseThrow();
+ final var person = personRepo.findPersonByOptionalNameLike("Smith").stream()
+ .filter(p -> p.getPersonType() == NATURAL_PERSON)
+ .findFirst().orElseThrow();
// when
final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
@@ -158,15 +182,18 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// then
allTheseRelationsAreReturned(
result,
- "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP Second e.K.', contact='second contact')",
- "rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')");
+ "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Smith, Peter', contact='sixth contact')",
+ "rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')",
+ "rel(anchor='IF Third OHG', type='SUBSCRIBER', mark='members-announce', holder='NP Smith, Peter', contact='third contact')");
}
@Test
public void normalUser_canViewRelationsOfOwnedPersons() {
// given:
- context("person-FirstGmbH@example.com");
- final var person = personRepo.findPersonByOptionalNameLike("First").stream().findFirst().orElseThrow();
+ context("person-SmithPeter@example.com");
+ final var person = personRepo.findPersonByOptionalNameLike("Smith").stream()
+ .filter(p -> p.getPersonType() == NATURAL_PERSON)
+ .findFirst().orElseThrow();
// when:
final var result = relationRepo.findRelationRelatedToPersonUuid(person.getUuid());
@@ -174,8 +201,10 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// then:
exactlyTheseRelationsAreReturned(
result,
- "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP First GmbH', contact='first contact')",
- "rel(anchor='LP First GmbH', type='REPRESENTATIVE', holder='NP Firby, Susan', contact='first contact')");
+ "rel(anchor='LP Second e.K.', type='REPRESENTATIVE', holder='NP Smith, Peter', contact='second contact')",
+ "rel(anchor='IF Third OHG', type='SUBSCRIBER', mark='members-announce', holder='NP Smith, Peter', contact='third contact')",
+ "rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Smith, Peter', contact='sixth contact')",
+ "rel(anchor='NP Smith, Peter', type='DEBITOR', holder='NP Smith, Peter', contact='third contact')");
}
}
@@ -187,13 +216,13 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// given
context("superuser-alex@hostsharing.net");
final var givenRelation = givenSomeTemporaryRelationBessler(
- "Anita", "fifth contact");
+ "Bert", "fifth contact");
assertThatRelationIsVisibleForUserWithRole(
givenRelation,
"hs_office_person#ErbenBesslerMelBessler.admin");
assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net");
- final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
+ final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow();
// when
final var result = jpaAttempt.transacted(() -> {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
index 67a731de..ad94ca9d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
@@ -24,6 +24,7 @@ import jakarta.persistence.PersistenceContext;
import java.time.LocalDate;
import java.util.UUID;
+import static java.util.Optional.ofNullable;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat;
@@ -70,35 +71,27 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
+ .log().all()
.body("", lenientlyEquals("""
[
{
- "debitor": {
- "debitorNumber": 1000212,
- "billingContact": { "label": "second contact" }
- },
- "bankAccount": { "holder": "Second e.K." },
- "reference": "refSeconde.K.",
- "validFrom": "2022-10-01",
- "validTo": "2026-12-31"
- },
- {
- "debitor": {
- "debitorNumber": 1000111,
- "billingContact": { "label": "first contact" }
- },
+ "debitor": { "debitorNumber": 1000111 },
"bankAccount": { "holder": "First GmbH" },
- "reference": "refFirstGmbH",
+ "reference": "ref-10001-11",
"validFrom": "2022-10-01",
"validTo": "2026-12-31"
},
{
- "debitor": {
- "debitorNumber": 1000313,
- "billingContact": { "label": "third contact" }
- },
+ "debitor": { "debitorNumber": 1000212 },
+ "bankAccount": { "holder": "Second e.K." },
+ "reference": "ref-10002-12",
+ "validFrom": "2022-10-01",
+ "validTo": "2026-12-31"
+ },
+ {
+ "debitor": { "debitorNumber": 1000313 },
"bankAccount": { "holder": "Third OHG" },
- "reference": "refThirdOHG",
+ "reference": "ref-10003-13",
"validFrom": "2022-10-01",
"validTo": "2026-12-31"
}
@@ -139,7 +132,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.statusCode(201)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("debitor.partner.person.tradeName", is("Third OHG"))
+ .body("debitor.partner.partnerNumber", is(10003))
.body("bankAccount.iban", is("DE02200505501015871393"))
.body("reference", is("temp ref CAT A"))
.body("validFrom", is("2022-10-13"))
@@ -262,15 +255,12 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "debitor": {
- "debitorNumber": 1000111,
- "billingContact": { "label": "first contact" }
- },
+ "debitor": { "debitorNumber": 1000111 },
"bankAccount": {
"holder": "First GmbH",
"iban": "DE02120300000000202051"
},
- "reference": "refFirstGmbH",
+ "reference": "ref-10001-11",
"validFrom": "2022-10-01",
"validTo": "2026-12-31"
}
@@ -314,15 +304,12 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.contentType("application/json")
.body("", lenientlyEquals("""
{
- "debitor": {
- "debitorNumber": 1000111,
- "billingContact": { "label": "first contact" }
- },
+ "debitor": { "debitorNumber": 1000111 },
"bankAccount": {
"holder": "First GmbH",
"iban": "DE02120300000000202051"
},
- "reference": "refFirstGmbH",
+ "reference": "ref-10001-11",
"validFrom": "2022-10-01",
"validTo": "2026-12-31"
}
@@ -337,7 +324,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
@Test
void globalAdmin_canPatchAllUpdatablePropertiesOfSepaMandate() {
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
final var location = RestAssured // @formatter:off
.given()
@@ -358,7 +345,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("debitor.partner.person.tradeName", is("First GmbH"))
+ .body("debitor.debitorNumber", is(1000111))
.body("bankAccount.iban", is("DE02120300000000202051"))
.body("reference", is("temp ref CAT Z - patched"))
.body("agreement", is("2020-06-01"))
@@ -370,7 +357,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
context.define("superuser-alex@hostsharing.net");
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> {
- assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LP First GmbH: fir)");
+ assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: rel(anchor='LP First GmbH', type='DEBITOR', holder='LP First GmbH'), fir)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched");
assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05");
@@ -383,7 +370,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
void globalAdmin_canPatchJustValidToOfArbitrarySepaMandate() {
context.define("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
final var location = RestAssured // @formatter:off
.given()
@@ -401,7 +388,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
.statusCode(200)
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
- .body("debitor.partner.person.tradeName", is("First GmbH"))
+ .body("debitor.debitorNumber", is(1000111))
.body("bankAccount.iban", is("DE02120300000000202051"))
.body("reference", is("temp ref CAT Z"))
.body("validFrom", is("2022-11-01"))
@@ -411,7 +398,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
// finally, the sepaMandate is actually updated
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> {
- assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LP First GmbH: fir)");
+ assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: rel(anchor='LP First GmbH', type='DEBITOR', holder='LP First GmbH'), fir)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z");
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)");
@@ -423,7 +410,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
void globalAdmin_canNotPatchReferenceOfArbitrarySepaMandate() {
context.define("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
final var location = RestAssured // @formatter:off
.given()
@@ -458,7 +445,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
@Test
void globalAdmin_canDeleteArbitrarySepaMandate() {
context.define("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
RestAssured // @formatter:off
.given()
@@ -477,7 +464,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
@Accepts({ "SepaMandate:X(Access Control)" })
void bankAccountAdminUser_canNotDeleteRelatedSepaMandate() {
context.define("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
RestAssured // @formatter:off
.given()
@@ -496,7 +483,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
@Accepts({ "SepaMandate:X(Access Control)" })
void normalUser_canNotDeleteUnrelatedSepaMandate() {
context.define("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandate();
+ final var givenSepaMandate = givenSomeTemporarySepaMandateForDebitorNumber(1000111);
RestAssured // @formatter:off
.given()
@@ -512,11 +499,13 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
}
}
- private HsOfficeSepaMandateEntity givenSomeTemporarySepaMandate() {
+ private HsOfficeSepaMandateEntity givenSomeTemporarySepaMandateForDebitorNumber(final int debitorNumber) {
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
- final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
- final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike("First").get(0);
+ final var givenDebitor = debitorRepo.findDebitorByDebitorNumber(debitorNumber).get(0);
+ final var bankAccountHolder = ofNullable(givenDebitor.getPartner().getPartnerRel().getHolder().getTradeName())
+ .orElse(givenDebitor.getPartner().getPartnerRel().getHolder().getFamilyName());
+ final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike(bankAccountHolder).get(0);
final var newSepaMandate = HsOfficeSepaMandateEntity.builder()
.uuid(UUID.randomUUID())
.debitor(givenDebitor)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
index 79910d28..9ffa28f2 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
@@ -26,6 +26,7 @@ import java.util.List;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
+import static net.hostsharing.test.Array.fromFormatted;
import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat;
@@ -94,8 +95,6 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
- .map(s -> s.replace("-firstcontact", "-..."))
- .map(s -> s.replace("PaulWinkler", "Paul..."))
.map(s -> s.replace("hs_office_", ""))
.toList();
@@ -118,41 +117,36 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_sepamandate#temprefB.owner",
- "hs_office_sepamandate#temprefB.admin",
- "hs_office_sepamandate#temprefB.agent",
- "hs_office_sepamandate#temprefB.tenant",
- "hs_office_sepamandate#temprefB.guest"));
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
- .map(s -> s.replace("-firstcontact", "-..."))
- .map(s -> s.replace("PaulWinkler", "Paul..."))
.map(s -> s.replace("hs_office_", ""))
- .containsExactlyInAnyOrder(Array.fromFormatted(
+ .containsExactlyInAnyOrder(fromFormatted(
initialGrantNames,
// owner
- "{ grant perm DELETE on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
- "{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }",
+ "{ grant perm DELETE on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner to role global#global.admin by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner to user superuser-alex@hostsharing.net by sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner and assume }",
// admin
- "{ grant perm UPDATE on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
- "{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }",
- "{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }",
+ "{ grant perm UPDATE on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner by system and assume }",
// agent
- "{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }",
- "{ grant role debitor#1000111:FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }",
- "{ grant role sepamandate#temprefB.agent to role bankaccount#Paul....admin by system and assume }",
- "{ grant role sepamandate#temprefB.agent to role debitor#1000111:FirstGmbH-....admin by system and assume }",
+ "{ grant role bankaccount#DE02600501010002034304.referrer to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FirstGmbH.agent to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
- // tenant
- "{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }",
- "{ grant role debitor#1000111:FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }",
- "{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
+ // referrer
+ "{ grant perm SELECT on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role bankaccount#DE02600501010002034304.admin by system and assume }",
+ "{ grant role relation#FirstGmbH-with-DEBITOR-FirstGmbH.tenant to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer by system and assume }",
+ "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role relation#FirstGmbH-with-DEBITOR-FirstGmbH.agent by system and assume }",
- // guest
- "{ grant perm SELECT on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }",
- "{ grant role sepamandate#temprefB.guest to role sepamandate#temprefB.tenant by system and assume }",
null));
}
@@ -176,9 +170,9 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
// then
allTheseSepaMandatesAreReturned(
result,
- "SEPA-Mandate(DE02120300000000202051, refFirstGmbH, 2022-09-30, [2022-10-01,2027-01-01))",
- "SEPA-Mandate(DE02100500000054540402, refSeconde.K., 2022-09-30, [2022-10-01,2027-01-01))",
- "SEPA-Mandate(DE02300209000106531065, refThirdOHG, 2022-09-30, [2022-10-01,2027-01-01))");
+ "SEPA-Mandate(DE02100500000054540402, ref-10002-12, 2022-09-30, [2022-10-01,2027-01-01))",
+ "SEPA-Mandate(DE02120300000000202051, ref-10001-11, 2022-09-30, [2022-10-01,2027-01-01))",
+ "SEPA-Mandate(DE02300209000106531065, ref-10003-13, 2022-09-30, [2022-10-01,2027-01-01))");
}
@Test
@@ -192,7 +186,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
// then:
exactlyTheseSepaMandatesAreReturned(
result,
- "SEPA-Mandate(DE02120300000000202051, refFirstGmbH, 2022-09-30, [2022-10-01,2027-01-01))");
+ "SEPA-Mandate(DE02120300000000202051, ref-10001-11, 2022-09-30, [2022-10-01,2027-01-01))");
}
}
@@ -210,9 +204,9 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
// then
exactlyTheseSepaMandatesAreReturned(
result,
- "SEPA-Mandate(DE02120300000000202051, refFirstGmbH, 2022-09-30, [2022-10-01,2027-01-01))",
- "SEPA-Mandate(DE02100500000054540402, refSeconde.K., 2022-09-30, [2022-10-01,2027-01-01))",
- "SEPA-Mandate(DE02300209000106531065, refThirdOHG, 2022-09-30, [2022-10-01,2027-01-01))");
+ "SEPA-Mandate(DE02100500000054540402, ref-10002-12, 2022-09-30, [2022-10-01,2027-01-01))",
+ "SEPA-Mandate(DE02120300000000202051, ref-10001-11, 2022-09-30, [2022-10-01,2027-01-01))",
+ "SEPA-Mandate(DE02300209000106531065, ref-10003-13, 2022-09-30, [2022-10-01,2027-01-01))");
}
@Test
@@ -226,7 +220,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
// then
exactlyTheseSepaMandatesAreReturned(
result,
- "SEPA-Mandate(DE02300209000106531065, refThirdOHG, 2022-09-30, [2022-10-01,2027-01-01))");
+ "SEPA-Mandate(DE02300209000106531065, ref-10003-13, 2022-09-30, [2022-10-01,2027-01-01))");
}
}
@@ -236,10 +230,10 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
@Test
public void hostsharingAdmin_canUpdateArbitrarySepaMandate() {
// given
- final var givenSepaMandate = givenSomeTemporarySepaMandateBessler("Peter Smith");
+ final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02600501010002034304");
assertThatSepaMandateIsVisibleForUserWithRole(
givenSepaMandate,
- "hs_office_bankaccount#PeterSmith.admin");
+ "hs_office_bankaccount#DE02600501010002034304.admin");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -264,16 +258,18 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
public void bankAccountAdmin_canViewButNotUpdateRelatedSepaMandate() {
// given
context("superuser-alex@hostsharing.net");
- final var givenSepaMandate = givenSomeTemporarySepaMandateBessler("Anita Bessler");
+
+ final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02300606010002474689");
assertThatSepaMandateIsVisibleForUserWithRole(
givenSepaMandate,
- "hs_office_bankaccount#AnitaBessler.admin");
+ "hs_office_bankaccount#DE02300606010002474689.admin");
assertThatSepaMandateActuallyInDatabase(givenSepaMandate);
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_bankaccount#AnitaBessler.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_bankaccount#DE02300606010002474689.admin");
+
givenSepaMandate.setValidity(Range.closedOpen(
givenSepaMandate.getValidity().lower(), newValidityEnd));
return toCleanup(sepaMandateRepo.save(givenSepaMandate));
@@ -317,7 +313,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
public void globalAdmin_withoutAssumedRole_canDeleteAnySepaMandate() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenSepaMandate = givenSomeTemporarySepaMandateBessler("Fourth eG");
+ final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02200505501015871393");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -337,7 +333,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
public void nonGlobalAdmin_canNotDeleteTheirRelatedSepaMandate() {
// given
context("superuser-alex@hostsharing.net", null);
- final var givenSepaMandate = givenSomeTemporarySepaMandateBessler("Third OHG");
+ final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02300209000106531065");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -363,11 +359,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll()));
- final var givenSepaMandate = givenSomeTemporarySepaMandateBessler("Mel Bessler");
- assertThat(distinctRoleNamesOf(rawRoleRepo.findAll()).size()).as("precondition failed: unexpected number of roles created")
- .isEqualTo(initialRoleNames.length + 5);
- assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()).size()).as("precondition failed: unexpected number of grants created")
- .isEqualTo(initialGrantNames.length + 14);
+ final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02600501010002034304");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -397,15 +389,16 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
- "[creating SEPA-mandate test-data FirstGmbH, hs_office_sepamandate, INSERT]",
- "[creating SEPA-mandate test-data Seconde.K., hs_office_sepamandate, INSERT]");
+ "[creating SEPA-mandate test-data 1000111, hs_office_sepamandate, INSERT]",
+ "[creating SEPA-mandate test-data 1000212, hs_office_sepamandate, INSERT]",
+ "[creating SEPA-mandate test-data 1000313, hs_office_sepamandate, INSERT]");
}
- private HsOfficeSepaMandateEntity givenSomeTemporarySepaMandateBessler(final String bankAccountHolder) {
+ private HsOfficeSepaMandateEntity givenSomeTemporarySepaMandate(final String iban) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0);
- final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike(bankAccountHolder).get(0);
+ final var givenBankAccount = bankAccountRepo.findByIbanOrderByIbanAsc(iban).get(0);
final var newSepaMandate = HsOfficeSepaMandateEntity.builder()
.debitor(givenDebitor)
.bankAccount(givenBankAccount)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
index 968e5416..722fd87e 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
@@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository;
+import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
@@ -17,6 +18,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import jakarta.persistence.*;
+import java.lang.reflect.Method;
import java.util.*;
import static java.lang.System.out;
@@ -56,6 +58,14 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
private Set initialRbacRoles;
private Set initialRbacGrants;
+ private TestInfo testInfo;
+
+ public T refresh(final T entity) {
+ final var merged = em.merge(entity);
+ em.refresh(merged);
+ return merged;
+ }
+
public UUID toCleanup(final Class extends HasUuid> entityClass, final UUID uuidToCleanup) {
out.println("toCleanup(" + entityClass.getSimpleName() + ", " + uuidToCleanup);
entitiesToCleanup.put(uuidToCleanup, entityClass);
@@ -152,6 +162,11 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return currentCount;
}
+ @BeforeEach
+ void keepTestInfo(final TestInfo testInfo) {
+ this.testInfo = testInfo;
+ }
+
@AfterEach
void cleanupAndCheckCleanup(final TestInfo testInfo) {
out.println(ContextBasedTestWithCleanup.class.getSimpleName() + ".cleanupAndCheckCleanup");
@@ -254,6 +269,29 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
.collect(toSet());
}).assertSuccessful().returnedValue();
}
+
+ /**
+ * Generates a diagram of the RBAC-Grants to the current subjects (user or assumed roles).
+ */
+ protected void generateRbacDiagramForCurrentSubjects(final EnumSet include) {
+ final var title = testInfo.getTestMethod().map(Method::getName).orElseThrow();
+ RbacGrantsDiagramService.writeToFile(
+ title,
+ diagramService.allGrantsToCurrentUser(include),
+ "doc/" + title + ".md"
+ );
+ }
+
+ /**
+ * Generates a diagram of the RBAC-Grants for the given object and permission.
+ */
+ protected void generateRbacDiagramForObjectPermission(final UUID targetObject, final String rbacOp, final String name) {
+ RbacGrantsDiagramService.writeToFile(
+ name,
+ diagramService.allGrantsFrom(targetObject, rbacOp, RbacGrantsDiagramService.Include.ALL),
+ "doc/temp/" + name + ".md"
+ );
+ }
}
interface RbacObjectRepository extends Repository {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java b/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
new file mode 100644
index 00000000..1699a5d2
--- /dev/null
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
@@ -0,0 +1,15 @@
+package net.hostsharing.hsadminng.hs.office.test;
+
+import net.hostsharing.hsadminng.persistence.HasUuid;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class EntityList {
+
+ public static E one(final List entities) {
+ assertThat(entities).hasSize(1);
+ return entities.stream().findFirst().orElseThrow();
+ }
+}
From f8fb273918ddaf86f5dc4aaa5949edf3ef048238 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 11:04:56 +0200
Subject: [PATCH 07/12] generated RBAC for coopshares and -assets (#27)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/27
Reviewed-by: Timotheus Pokorra
---
.../HsOfficeCoopAssetsTransactionEntity.java | 45 ++-
.../HsOfficeCoopSharesTransactionEntity.java | 46 +++-
.../membership/HsOfficeMembershipEntity.java | 9 +-
.../resources/db/changelog/010-context.sql | 10 +-
.../resources/db/changelog/020-audit-log.sql | 2 +-
.../303-hs-office-membership-rbac.md | 14 +-
.../303-hs-office-membership-rbac.sql | 12 +-
.../313-hs-office-coopshares-rbac.md | 257 ++++++++++++++++--
.../313-hs-office-coopshares-rbac.sql | 174 +++++++-----
.../323-hs-office-coopassets-rbac.md | 257 ++++++++++++++++--
.../323-hs-office-coopassets-rbac.sql | 174 +++++++-----
...sTransactionRepositoryIntegrationTest.java | 5 +-
...sTransactionRepositoryIntegrationTest.java | 3 +-
...iceMembershipControllerAcceptanceTest.java | 4 +-
...ceMembershipRepositoryIntegrationTest.java | 28 +-
15 files changed, 809 insertions(+), 231 deletions(-)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
index 0b579a85..03d3ae49 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
@@ -1,21 +1,44 @@
package net.hostsharing.hsadminng.hs.office.coopassets;
-import lombok.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
-import jakarta.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Optional;
import java.util.UUID;
import static java.util.Optional.ofNullable;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -89,4 +112,22 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
public String toShortString() {
return "%s:%+1.2f".formatted(getTaggedMemberNumber(), Optional.ofNullable(assetValue).orElse(BigDecimal.ZERO));
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("coopAssetsTransaction", HsOfficeCoopAssetsTransactionEntity.class)
+ .withIdentityView(RbacView.SQL.projection("reference"))
+ .withUpdatableColumns("comment")
+ .importEntityAlias("membership", HsOfficeMembershipEntity.class,
+ dependsOnColumn("membershipUuid"),
+ directlyFetchedByDependsOnColumn(),
+ NOT_NULL)
+
+ .toRole("membership", ADMIN).grantPermission(INSERT)
+ .toRole("membership", ADMIN).grantPermission(UPDATE)
+ .toRole("membership", AGENT).grantPermission(SELECT);
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("323-hs-office-coopassets-rbac");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
index 807af25f..52222582 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
@@ -1,17 +1,41 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
-import lombok.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
-import jakarta.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
import static java.util.Optional.ofNullable;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity
@@ -83,4 +107,22 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu
public String toShortString() {
return "%s%+d".formatted(getMemberNumberTagged(), shareCount);
}
+
+ public static RbacView rbac() {
+ return rbacViewFor("coopSharesTransaction", HsOfficeCoopSharesTransactionEntity.class)
+ .withIdentityView(SQL.projection("reference"))
+ .withUpdatableColumns("comment")
+ .importEntityAlias("membership", HsOfficeMembershipEntity.class,
+ dependsOnColumn("membershipUuid"),
+ directlyFetchedByDependsOnColumn(),
+ NOT_NULL)
+
+ .toRole("membership", ADMIN).grantPermission(INSERT)
+ .toRole("membership", ADMIN).grantPermission(UPDATE)
+ .toRole("membership", AGENT).grantPermission(SELECT);
+ }
+
+ public static void main(String[] args) throws IOException {
+ rbac().generateWithBaseFileName("313-hs-office-coopshares-rbac");
+ }
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index c4a4c8b9..b38d92b9 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -25,7 +25,6 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -142,14 +141,14 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
.createRole(OWNER, (with) -> {
with.owningUser(CREATOR);
- with.incomingSuperRole("partnerRel", ADMIN);
- with.permission(DELETE);
})
.createSubRole(ADMIN, (with) -> {
- with.incomingSuperRole("partnerRel", AGENT);
+ with.incomingSuperRole("partnerRel", ADMIN);
+ with.permission(DELETE);
with.permission(UPDATE);
})
- .createSubRole(REFERRER, (with) -> {
+ .createSubRole(AGENT, (with) -> {
+ with.incomingSuperRole("partnerRel", AGENT);
with.outgoingSubRole("partnerRel", TENANT);
with.permission(SELECT);
});
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql
index 66ebacc3..ba655e93 100644
--- a/src/main/resources/db/changelog/010-context.sql
+++ b/src/main/resources/db/changelog/010-context.sql
@@ -23,7 +23,7 @@ end; $$;
Defines the transaction context.
*/
create or replace procedure defineContext(
- currentTask varchar(96),
+ currentTask varchar(127),
currentRequest text = null,
currentUser varchar(63) = null,
assumedRoles varchar(1023) = null
@@ -31,8 +31,8 @@ create or replace procedure defineContext(
language plpgsql as $$
begin
currentTask := coalesce(currentTask, '');
- assert length(currentTask) <= 96, FORMAT('currentTask must not be longer than 96 characters: "%s"', currentTask);
- assert length(currentTask) > 8, FORMAT('currentTask must be at least 8 characters long: "%s""', currentTask);
+ assert length(currentTask) <= 127, FORMAT('currentTask must not be longer than 127 characters: "%s"', currentTask);
+ assert length(currentTask) >= 12, FORMAT('currentTask must be at least 12 characters long: "%s""', currentTask);
execute format('set local hsadminng.currentTask to %L', currentTask);
currentRequest := coalesce(currentRequest, '');
@@ -59,11 +59,11 @@ end; $$;
Raises exception if not set.
*/
create or replace function currentTask()
- returns varchar(96)
+ returns varchar(127)
stable -- leakproof
language plpgsql as $$
declare
- currentTask varchar(96);
+ currentTask varchar(127);
begin
begin
currentTask := current_setting('hsadminng.currentTask');
diff --git a/src/main/resources/db/changelog/020-audit-log.sql b/src/main/resources/db/changelog/020-audit-log.sql
index 2491218d..4c2826e3 100644
--- a/src/main/resources/db/changelog/020-audit-log.sql
+++ b/src/main/resources/db/changelog/020-audit-log.sql
@@ -28,7 +28,7 @@ create table tx_context
txTimestamp timestamp not null,
currentUser varchar(63) not null, -- not the uuid, because users can be deleted
assumedRoles varchar(1023) not null, -- not the uuids, because roles can be deleted
- currentTask varchar(96) not null,
+ currentTask varchar(127) not null,
currentRequest text not null
);
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
index 4f425f6e..339f9eb0 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
@@ -81,7 +81,7 @@ subgraph membership["`**membership**`"]
role:membership:owner[[membership:owner]]
role:membership:admin[[membership:admin]]
- role:membership:referrer[[membership:referrer]]
+ role:membership:agent[[membership:agent]]
end
subgraph membership:permissions[ ]
@@ -144,16 +144,16 @@ 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:membership:owner
role:membership:owner ==> role:membership:admin
-role:partnerRel:agent ==> role:membership:admin
-role:membership:admin ==> role:membership:referrer
-role:membership:referrer ==> role:partnerRel:tenant
+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:owner ==> perm:membership:DELETE
+role:membership:admin ==> perm:membership:DELETE
role:membership:admin ==> perm:membership:UPDATE
-role:membership:referrer ==> perm:membership:SELECT
+role:membership:agent ==> perm:membership:SELECT
```
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
index 17dbc84c..4f34cee8 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
@@ -45,23 +45,23 @@ begin
perform createRoleWithGrants(
hsOfficeMembershipOwner(NEW),
- permissions => array['DELETE'],
- incomingSuperRoles => array[hsOfficeRelationAdmin(newPartnerRel)],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
hsOfficeMembershipAdmin(NEW),
- permissions => array['UPDATE'],
+ permissions => array['DELETE', 'UPDATE'],
incomingSuperRoles => array[
hsOfficeMembershipOwner(NEW),
- hsOfficeRelationAgent(newPartnerRel)]
+ hsOfficeRelationAdmin(newPartnerRel)]
);
perform createRoleWithGrants(
- hsOfficeMembershipReferrer(NEW),
+ hsOfficeMembershipAgent(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeMembershipAdmin(NEW)],
+ incomingSuperRoles => array[
+ hsOfficeMembershipAdmin(NEW),
+ hsOfficeRelationAgent(newPartnerRel)],
outgoingSubRoles => array[hsOfficeRelationTenant(newPartnerRel)]
);
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
index 4093eb2d..70f268a8 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
@@ -1,29 +1,250 @@
-### hs_office_coopSharesTransaction RBAC
+### rbac coopSharesTransaction
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph hsOfficeMembership
+subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPerson**`"]
direction TB
- style hsOfficeMembership fill:#eee
-
- role:hsOfficeMembership.owner[membership.admin]
- --> role:hsOfficeMembership.admin[membership.admin]
- --> role:hsOfficeMembership.agent[membership.agent]
- --> role:hsOfficeMembership.tenant[membership.tenant]
- --> role:hsOfficeMembership.guest[membership.guest]
-
- role:hsOfficePartner.agent --> role:hsOfficeMembership.agent
+ 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 hsOfficeCoopSharesTransaction
-
- role:hsOfficeMembership.admin
- --> perm:hsOfficeCoopSharesTransaction.create{{coopSharesTx.create}}
-
- role:hsOfficeMembership.agent
- --> perm:hsOfficeCoopSharesTransaction.view{{coopSharesTx.view}}
+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.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 membership.partnerRel["`**membership.partnerRel**`"]
+ direction TB
+ style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+ 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 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
+
+ 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
+
+ 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.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 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
+
+ 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
```
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
index a4cac136..2cdfa55c 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
@@ -1,125 +1,151 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-OBJECT:1 endDelimiter:--//
+--changeset hs-office-coopsharestransaction-rbac-OBJECT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRelatedRbacObject('hs_office_coopSharesTransaction');
+call generateRelatedRbacObject('hs_office_coopsharestransaction');
--//
-- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
+--changeset hs-office-coopsharestransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRoleDescriptors('hsOfficeCoopSharesTransaction', 'hs_office_coopSharesTransaction');
+call generateRbacRoleDescriptors('hsOfficeCoopSharesTransaction', 'hs_office_coopsharestransaction');
--//
-- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-coopsharestransaction-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the permissions for coopSharesTransaction entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeCoopSharesTransactionRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeCoopSharesTransaction(
+ NEW hs_office_coopsharestransaction
+)
+ language plpgsql as $$
+
declare
- newHsOfficeMembership hs_office_membership;
+ newMembership hs_office_membership;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- select * from hs_office_membership as p where p.uuid = NEW.membershipUuid into newHsOfficeMembership;
+ 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);
- if TG_OP = 'INSERT' then
-
- -- Each coopSharesTransaction entity belong exactly to one membership entity
- -- and it makes little sense just to delegate coopSharesTransaction roles.
- -- Therefore, we do not create coopSharesTransaction roles at all,
- -- but instead just assign extra permissions to existing membership-roles.
-
- -- coopsharestransactions cannot be edited nor deleted, just created+viewed
- call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
- createPermissions(NEW.uuid, array ['SELECT'])
- );
-
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ 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; $$;
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-create trigger createRbacRolesForHsOfficeCoopSharesTransaction_Trigger
- after insert
- on hs_office_coopSharesTransaction
+create trigger insertTriggerForHsOfficeCoopSharesTransaction_tg
+ after insert on hs_office_coopsharestransaction
for each row
-execute procedure hsOfficeCoopSharesTransactionRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficeCoopSharesTransaction_tf();
--//
-- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-coopsharestransaction-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_coopSharesTransaction', 'target.reference');
---//
-
--- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_coopSharesTransaction', orderby => 'target.reference');
---//
-
-
--- ============================================================================
---changeset hs-office-coopSharesTransaction-rbac-NEW-CoopSharesTransaction:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
/*
- Creates a global permission for new-coopSharesTransaction and assigns it to the hostsharing admins role.
+ Creates INSERT INTO hs_office_coopsharestransaction permissions for the related hs_office_membership rows.
*/
do language plpgsql $$
declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
+ row hs_office_membership;
begin
- call defineContext('granting global new-coopSharesTransaction permission to global admin role', null, null, null);
+ call defineContext('create INSERT INTO hs_office_coopsharestransaction permissions for the related hs_office_membership rows');
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-coopsharestransaction']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
+ FOR row IN SELECT * FROM hs_office_membership
+ LOOP
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', 'hs_office_coopsharestransaction'),
+ hsOfficeMembershipAdmin(row));
+ END LOOP;
+ END;
$$;
/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeCoopSharesTransactionNotAllowedForCurrentSubjects()
+ 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
-as $$
+ language plpgsql
+ strict as $$
begin
- raise exception '[403] new-coopsharestransaction not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
+ call grantPermissionToRole(
+ createPermission(NEW.uuid, 'INSERT', 'hs_office_coopsharestransaction'),
+ hsOfficeMembershipAdmin(NEW));
+ return NEW;
end; $$;
-/**
- Checks if the user or assumed roles are allowed to create a new customer.
- */
-create trigger hs_office_coopSharesTransaction_insert_trigger
- before insert
- on hs_office_coopSharesTransaction
+-- 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
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeCoopSharesTransactionNotAllowedForCurrentSubjects();
+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$);
--//
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
index 94ce746a..210bd69f 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
@@ -1,29 +1,250 @@
-### hs_office_coopAssetsTransaction RBAC
+### rbac coopAssetsTransaction
+
+This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
```mermaid
+%%{init:{'flowchart':{'htmlLabels':false}}}%%
flowchart TB
-subgraph hsOfficeMembership
+subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPerson**`"]
direction TB
- style hsOfficeMembership fill:#eee
-
- role:hsOfficeMembership.owner[membership.admin]
- --> role:hsOfficeMembership.admin[membership.admin]
- --> role:hsOfficeMembership.agent[membership.agent]
- --> role:hsOfficeMembership.tenant[membership.tenant]
- --> role:hsOfficeMembership.guest[membership.guest]
-
- role:hsOfficePartner.agent --> role:hsOfficeMembership.agent
+ 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 hsOfficeCoopAssetsTransaction
-
- role:hsOfficeMembership.admin
- --> perm:hsOfficeCoopAssetsTransaction.create{{coopAssetsTx.create}}
-
- role:hsOfficeMembership.agent
- --> perm:hsOfficeCoopAssetsTransaction.view{{coopAssetsTx.view}}
+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.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 membership.partnerRel["`**membership.partnerRel**`"]
+ direction TB
+ style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
+ 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 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
+
+ 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
+
+ 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.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 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
+
+ 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
```
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
index 035da07b..4dda4e2e 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
@@ -1,125 +1,151 @@
--liquibase formatted sql
+-- This code generated was by RbacViewPostgresGenerator, do not amend manually.
+
-- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-OBJECT:1 endDelimiter:--//
+--changeset hs-office-coopassetstransaction-rbac-OBJECT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRelatedRbacObject('hs_office_coopAssetsTransaction');
+call generateRelatedRbacObject('hs_office_coopassetstransaction');
--//
-- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
+--changeset hs-office-coopassetstransaction-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacRoleDescriptors('hsOfficeCoopAssetsTransaction', 'hs_office_coopAssetsTransaction');
+call generateRbacRoleDescriptors('hsOfficeCoopAssetsTransaction', 'hs_office_coopassetstransaction');
--//
-- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-ROLES-CREATION:1 endDelimiter:--//
+--changeset hs-office-coopassetstransaction-rbac-insert-trigger:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
- Creates and updates the permissions for coopAssetsTransaction entities.
+ Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/
-create or replace function hsOfficeCoopAssetsTransactionRbacRolesTrigger()
- returns trigger
- language plpgsql
- strict as $$
+create or replace procedure buildRbacSystemForHsOfficeCoopAssetsTransaction(
+ NEW hs_office_coopassetstransaction
+)
+ language plpgsql as $$
+
declare
- newHsOfficeMembership hs_office_membership;
+ newMembership hs_office_membership;
+
begin
call enterTriggerForObjectUuid(NEW.uuid);
- select * from hs_office_membership as p where p.uuid = NEW.membershipUuid into newHsOfficeMembership;
+ 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);
- if TG_OP = 'INSERT' then
-
- -- Each coopAssetsTransaction entity belong exactly to one membership entity
- -- and it makes little sense just to delegate coopAssetsTransaction roles.
- -- Therefore, we do not create coopAssetsTransaction roles at all,
- -- but instead just assign extra permissions to existing membership-roles.
-
- -- coopassetstransactions cannot be edited nor deleted, just created+viewed
- call grantPermissionsToRole(
- getRoleId(hsOfficeMembershipReferrer(newHsOfficeMembership)),
- createPermissions(NEW.uuid, array ['SELECT'])
- );
-
- else
- raise exception 'invalid usage of TRIGGER';
- end if;
+ 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; $$;
-/*
- An AFTER INSERT TRIGGER which creates the role structure for a new customer.
- */
-create trigger createRbacRolesForHsOfficeCoopAssetsTransaction_Trigger
- after insert
- on hs_office_coopAssetsTransaction
+create trigger insertTriggerForHsOfficeCoopAssetsTransaction_tg
+ after insert on hs_office_coopassetstransaction
for each row
-execute procedure hsOfficeCoopAssetsTransactionRbacRolesTrigger();
+execute procedure insertTriggerForHsOfficeCoopAssetsTransaction_tf();
--//
-- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
+--changeset hs-office-coopassetstransaction-rbac-INSERT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
-call generateRbacIdentityViewFromProjection('hs_office_coopAssetsTransaction', 'target.reference');
---//
-
--- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-call generateRbacRestrictedView('hs_office_coopAssetsTransaction', orderby => 'target.reference');
---//
-
-
--- ============================================================================
---changeset hs-office-coopAssetsTransaction-rbac-NEW-CoopAssetsTransaction:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
/*
- Creates a global permission for new-coopAssetsTransaction and assigns it to the hostsharing admins role.
+ Creates INSERT INTO hs_office_coopassetstransaction permissions for the related hs_office_membership rows.
*/
do language plpgsql $$
declare
- addCustomerPermissions uuid[];
- globalObjectUuid uuid;
- globalAdminRoleUuid uuid ;
+ row hs_office_membership;
begin
- call defineContext('granting global new-coopAssetsTransaction permission to global admin role', null, null, null);
+ call defineContext('create INSERT INTO hs_office_coopassetstransaction permissions for the related hs_office_membership rows');
- globalAdminRoleUuid := findRoleId(globalAdmin());
- globalObjectUuid := (select uuid from global);
- addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-coopassetstransaction']);
- call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
- end;
+ FOR row IN SELECT * FROM hs_office_membership
+ LOOP
+ call grantPermissionToRole(
+ createPermission(row.uuid, 'INSERT', 'hs_office_coopassetstransaction'),
+ hsOfficeMembershipAdmin(row));
+ END LOOP;
+ END;
$$;
/**
- Used by the trigger to prevent the add-customer to current user respectively assumed roles.
- */
-create or replace function addHsOfficeCoopAssetsTransactionNotAllowedForCurrentSubjects()
+ 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
-as $$
+ language plpgsql
+ strict as $$
begin
- raise exception '[403] new-coopassetstransaction not permitted for %',
- array_to_string(currentSubjects(), ';', 'null');
+ call grantPermissionToRole(
+ createPermission(NEW.uuid, 'INSERT', 'hs_office_coopassetstransaction'),
+ hsOfficeMembershipAdmin(NEW));
+ return NEW;
end; $$;
-/**
- Checks if the user or assumed roles are allowed to create a new customer.
- */
-create trigger hs_office_coopAssetsTransaction_insert_trigger
- before insert
- on hs_office_coopAssetsTransaction
+-- 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
- when ( not hasAssumedRole() )
-execute procedure addHsOfficeCoopAssetsTransactionNotAllowedForCurrentSubjects();
+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$);
--//
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
index 90ab1f00..d6607501 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
@@ -89,7 +89,6 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
- .map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.toList();
@@ -110,11 +109,11 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(initialRoleNames)); // no new roles created
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
- .map(s -> s.replace("FirstGmbH-firstcontact", "..."))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
+ "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#M-1000101.agent by system and assume }",
+ "{ grant perm UPDATE on coopassetstransaction#temprefB to role membership#M-1000101.admin by system and assume }",
null));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
index 837e02fd..ed649f15 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
@@ -111,7 +111,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#M-1000101.referrer by system and assume }",
+ "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#M-1000101.agent by system and assume }",
+ "{ grant perm UPDATE on coopsharestransaction#temprefB to role membership#M-1000101.admin by system and assume }",
null));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
index c0d69951..51ad5b4c 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
@@ -335,10 +335,10 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
}
@Test
- void partnerRelAgent_canPatchValidityOfRelatedMembership() {
+ void partnerRelAdmin_canPatchValidityOfRelatedMembership() {
// given
- final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent";
+ final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.admin";
context.define("superuser-alex@hostsharing.net", givenPartnerAgent);
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
index a53b2705..fcf2e976 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
@@ -113,29 +113,31 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
initialRoleNames,
"hs_office_membership#M-1000117.admin",
"hs_office_membership#M-1000117.owner",
- "hs_office_membership#M-1000117.referrer"));
+ "hs_office_membership#M-1000117.agent"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
+ // insert
+ "{ grant perm INSERT into coopassetstransaction with membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
+ "{ grant perm INSERT into coopsharestransaction with membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
+
// owner
- "{ grant perm DELETE on membership#M-1000117 to role membership#M-1000117.owner by system and assume }",
+ "{ grant perm DELETE on membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
+ "{ grant role membership#M-1000117.owner to user superuser-alex@hostsharing.net by membership#M-1000117.owner and assume }",
// admin
"{ grant perm UPDATE on membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
"{ grant role membership#M-1000117.admin to role membership#M-1000117.owner by system and assume }",
- "{ grant role membership#M-1000117.owner to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
- "{ grant role membership#M-1000117.owner to user superuser-alex@hostsharing.net by membership#M-1000117.owner and assume }",
+ "{ grant role membership#M-1000117.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
// agent
- "{ grant role membership#M-1000117.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
-
- // referrer
- "{ grant perm SELECT on membership#M-1000117 to role membership#M-1000117.referrer by system and assume }",
- "{ grant role membership#M-1000117.referrer to role membership#M-1000117.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role membership#M-1000117.referrer by system and assume }",
+ "{ grant perm SELECT on membership#M-1000117 to role membership#M-1000117.agent by system and assume }",
+ "{ grant role membership#M-1000117.agent to role membership#M-1000117.admin by system and assume }",
+ "{ grant role membership#M-1000117.agent to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
+ "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role membership#M-1000117.agent by system and assume }",
null));
}
@@ -223,20 +225,20 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
}
@Test
- public void membershipReferrer_canViewButNotUpdateRelatedMembership() {
+ public void membershipAgent_canViewButNotUpdateRelatedMembership() {
// given
context("superuser-alex@hostsharing.net");
final var givenMembership = givenSomeTemporaryMembership("First", "13");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
assertThatMembershipIsVisibleForRole(
givenMembership,
- "hs_office_membership#M-1000113.referrer");
+ "hs_office_membership#M-1000113.agent");
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
// TODO: we should test with debitor- and partner-admin as well
- context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113.referrer");
+ context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113.agent");
givenMembership.setValidity(
Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);
From 7f418c12a129a578c20fd5fd94dcb3a82a73a81a Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 12:01:37 +0200
Subject: [PATCH 08/12] uniform idnames (#28)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/28
Reviewed-by: Timotheus Pokorra
---
README.md | 2 +-
doc/ideas/rbac-schema-f.md | 4 +-
doc/ideas/simplified-grant-structure.md | 4 +-
doc/rbac.md | 48 ++--
sql/rbac-tests.sql | 8 +-
sql/rbac-view-option-experiments.sql | 2 +-
.../membership/HsOfficeMembershipEntity.java | 5 +-
.../partner/HsOfficePartnerDetailsEntity.java | 2 +-
.../rbac/rbacdef/InsertTriggerGenerator.java | 4 +-
.../hsadminng/rbac/rbacdef/RbacView.java | 9 +-
.../RbacViewMermaidFlowchartGenerator.java | 2 +-
.../RolesGrantsAndPermissionsGenerator.java | 6 +-
.../rbac/rbacgrant/RbacGrantEntity.java | 6 +-
.../rbacgrant/RbacGrantsDiagramService.java | 20 +-
.../rbac/rbacrole/RbacRoleEntity.java | 2 +-
.../hsadminng/rbac/rbacrole/RbacRoleType.java | 2 +-
.../rbac/rbac-role-schemas.yaml | 10 +-
.../resources/db/changelog/010-context.sql | 3 +-
.../resources/db/changelog/050-rbac-base.sql | 4 +-
.../db/changelog/054-rbac-context.sql | 2 +-
.../resources/db/changelog/055-rbac-views.sql | 24 +-
.../db/changelog/058-rbac-generators.sql | 12 +-
.../db/changelog/080-rbac-global.sql | 8 +-
.../db/changelog/113-test-customer-rbac.md | 22 +-
.../db/changelog/113-test-customer-rbac.sql | 16 +-
.../changelog/118-test-customer-test-data.sql | 2 +-
.../db/changelog/123-test-package-rbac.md | 34 +--
.../db/changelog/123-test-package-rbac.sql | 26 +-
.../changelog/128-test-package-test-data.sql | 2 +-
.../db/changelog/133-test-domain-rbac.md | 59 ++---
.../db/changelog/133-test-domain-rbac.sql | 28 +-
.../changelog/203-hs-office-contact-rbac.md | 22 +-
.../changelog/203-hs-office-contact-rbac.sql | 16 +-
.../db/changelog/213-hs-office-person-rbac.md | 22 +-
.../changelog/213-hs-office-person-rbac.sql | 16 +-
.../changelog/223-hs-office-relation-rbac.md | 76 +++---
.../changelog/223-hs-office-relation-rbac.sql | 42 +--
.../228-hs-office-relation-test-data.sql | 2 +-
.../changelog/233-hs-office-partner-rbac.md | 118 +++------
.../changelog/233-hs-office-partner-rbac.sql | 40 +--
.../234-hs-office-partner-details-rbac.md | 2 +-
.../234-hs-office-partner-details-rbac.sql | 10 +-
.../238-hs-office-partner-test-data.sql | 2 +-
.../243-hs-office-bankaccount-rbac.md | 22 +-
.../243-hs-office-bankaccount-rbac.sql | 16 +-
.../253-hs-office-sepamandate-rbac.md | 153 +++++------
.../253-hs-office-sepamandate-rbac.sql | 30 +--
.../258-hs-office-sepamandate-test-data.sql | 2 +-
.../changelog/273-hs-office-debitor-rbac.md | 239 ++++++------------
.../changelog/273-hs-office-debitor-rbac.sql | 20 +-
.../278-hs-office-debitor-test-data.sql | 2 +-
.../303-hs-office-membership-rbac.md | 131 ++++------
.../303-hs-office-membership-rbac.sql | 20 +-
.../308-hs-office-membership-test-data.sql | 2 +-
.../313-hs-office-coopshares-rbac.md | 218 ++++------------
.../313-hs-office-coopshares-rbac.sql | 8 +-
.../323-hs-office-coopassets-rbac.md | 218 ++++------------
.../323-hs-office-coopassets-rbac.sql | 8 +-
.../context/ContextIntegrationTests.java | 12 +-
...eBankAccountRepositoryIntegrationTest.java | 20 +-
...fficeContactRepositoryIntegrationTest.java | 20 +-
...sTransactionRepositoryIntegrationTest.java | 6 +-
...sTransactionRepositoryIntegrationTest.java | 6 +-
...OfficeDebitorControllerAcceptanceTest.java | 2 +-
...fficeDebitorRepositoryIntegrationTest.java | 96 +++----
...iceMembershipControllerAcceptanceTest.java | 10 +-
...ceMembershipRepositoryIntegrationTest.java | 38 ++-
.../hs/office/migration/ImportOfficeData.java | 2 +-
...fficePartnerRepositoryIntegrationTest.java | 70 ++---
...OfficePersonRepositoryIntegrationTest.java | 24 +-
...ficeRelationRepositoryIntegrationTest.java | 56 ++--
...eSepaMandateRepositoryIntegrationTest.java | 40 +--
.../RbacGrantControllerAcceptanceTest.java | 90 +++----
.../rbacgrant/RbacGrantEntityUnitTest.java | 8 +-
.../RbacGrantRepositoryIntegrationTest.java | 56 ++--
...acGrantsDiagramServiceIntegrationTest.java | 32 +--
.../rbac/rbacrole/RawRbacRoleEntity.java | 2 +-
.../RbacRoleControllerAcceptanceTest.java | 54 ++--
.../rbacrole/RbacRoleControllerRestTest.java | 6 +-
.../RbacRoleRepositoryIntegrationTest.java | 84 +++---
.../hsadminng/rbac/rbacrole/TestRbacRole.java | 8 +-
.../RbacUserControllerAcceptanceTest.java | 18 +-
.../RbacUserRepositoryIntegrationTest.java | 176 ++++++-------
.../TestCustomerControllerAcceptanceTest.java | 8 +-
.../test/cust/TestCustomerEntityUnitTest.java | 22 +-
...TestCustomerRepositoryIntegrationTest.java | 8 +-
.../TestPackageControllerAcceptanceTest.java | 14 +-
.../test/pac/TestPackageEntityUnitTest.java | 34 +--
.../TestPackageRepositoryIntegrationTest.java | 10 +-
.../java/net/hostsharing/test/JpaAttempt.java | 5 +
90 files changed, 1207 insertions(+), 1665 deletions(-)
diff --git a/README.md b/README.md
index 04827ba3..23209dd2 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
curl \
- -H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy.admin' \
+ -H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
http://localhost:8080/api/test/packages
# add a new customer
diff --git a/doc/ideas/rbac-schema-f.md b/doc/ideas/rbac-schema-f.md
index 7047d066..f1731d4f 100644
--- a/doc/ideas/rbac-schema-f.md
+++ b/doc/ideas/rbac-schema-f.md
@@ -27,8 +27,8 @@ Objektorientiert gedacht, enthalten solche Objekte die Zusatzdaten einer Subklas
- Für die Rollenzuordnung zwischen referenzierten Objekten gilt:
- Für Objekte vom Typ Root werden die Rollen des zugehörigen Aggregator-Objektes verwendet.
- Gibt es Referenzen auf hierarchisch verbundene Objekte (z.B. Debitor.refundBankAccount) gilt folgende Faustregel:
- ***Nach oben absteigen, nach unten halten oder aufsteigen.*** An einem fachlich übergeordneten Objekt wird also eine niedrigere Rolle (z.B. Debitor-admin -> Partner.agent), einem fachlich untergeordneten Objekt eine gleichwertige Rolle (z.B. Partner.admin -> Debitor.admin) zugewiesen oder sogar aufgestiegen (Debitor.admin -> Package.tenant).
- - Für Referenzen zwischen Objekten, die nicht hierarchisch zueinander stehen (z.B. Debitor und Bankverbindung), wird auf beiden seiten abgestiegen (also Debitor.admin -> BankAccount.referrer und BankAccount.admin -> Debitor.tenant).
+ ***Nach oben absteigen, nach unten halten oder aufsteigen.*** An einem fachlich übergeordneten Objekt wird also eine niedrigere Rolle (z.B. Debitor.ADMIN -> Partner.AGENT), einem fachlich untergeordneten Objekt eine gleichwertige Rolle (z.B. Partner.ADMIN -> Debitor.ADMIN) zugewiesen oder sogar aufgestiegen (Debitor.ADMIN -> Package.TENANT).
+ - Für Referenzen zwischen Objekten, die nicht hierarchisch zueinander stehen (z.B. Debitor und Bankverbindung), wird auf beiden seiten abgestiegen (also Debitor.ADMIN -> BankAccount.REFERRER und BankAccount.ADMIN -> Debitor.TENANT).
Anmerkung: Der Typ-Begriff *Root* bezieht sich auf die Rolle im fachlichen Datenmodell. Im Bezug auf den Teilgraphen eines fachlichen Kontexts ist dies auch eine Wurzel im Sinne der Graphentheorie. Aber in anderen fachlichen Kontexten können auch diese Objekte von anderen Teilgraphen referenziert werden und werden dann zum inneren Knoten.
diff --git a/doc/ideas/simplified-grant-structure.md b/doc/ideas/simplified-grant-structure.md
index 6d89897a..d9b3cf44 100644
--- a/doc/ideas/simplified-grant-structure.md
+++ b/doc/ideas/simplified-grant-structure.md
@@ -16,11 +16,11 @@ Beim Debitor ist das nämlich selbst mit Generator die Hölle, zumal eben auch Q
Mit anderen Worten, um als Repräsentant eines Geschäftspartners auf den Bank-Account der Sepa-Mandate sehen zu dürfen, wird derzeut folgende Grant-Kette durchlaufen (bzw. eben noch nicht, weil es noch nicht funktioniert):
-User -> Partner-Holder-Person:Admin -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> BankAccount:Admin -> BankAccount:SELECT
+User -> Partner-Holder-Person:ADMIN -> Partner-Relation:AGENT -> Debitor-Relation:AGENT -> Sepa-Mandat:ADMIN -> BankAccount:ADMIN -> BankAccount:SELECT
Daraus würde:
-User -> Partner-Relation:Agent -> Debitor-Relation:Agent -> Sepa-Mandat:Admin -> Sepa-Mandat:SELECT*
+User -> Partner-Relation:AGENT -> Debitor-Relation:AGENT -> Sepa-Mandat:ADMIN -> Sepa-Mandat:SELECT*
(*mit JOIN auf RawBankAccount, also implizitem Leserecht)
diff --git a/doc/rbac.md b/doc/rbac.md
index 2de4d4bb..9e562148 100644
--- a/doc/rbac.md
+++ b/doc/rbac.md
@@ -196,24 +196,24 @@ E.g. if a new package is added, the admin-role of the related customer has to be
There can be global roles like 'administrators'.
Most roles, though, are specific for certain business-objects and automatically generated as such:
- business-object-table#business-object-name.relative-role
+ business-object-table#business-object-name.role-stereotype
Where *business-object-table* is the name of the SQL table of the business object (e.g *customer* or 'package'),
*business-object-name* is generated from an immutable business key(e.g. a prefix like 'xyz' or 'xyz00')
-and the *relative-role*' describes the role relative to the referenced business-object as follows:
+and the *role-stereotype* describes a role relative to a referenced business-object as follows:
#### owner
The owner-role is granted to the subject which created the business object.
-E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...admin'.
+E.g. for a new *customer* it would be granted to 'administrators' and for a new *package* to the 'customer#...:ADMIN'.
Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it.
In most cases, the permissions to other operations than 'DELETE' are granted through the 'admin' role.
By this, all roles ob sub-objects, which are assigned to the 'admin' role, are also granted to the 'owner'.
-#### admin
+#### ADMIN
The admin-role is granted to a role of those subjects who manage the business object.
E.g. a 'package' is manged by the admin of the customer.
@@ -222,7 +222,7 @@ Whoever has the admin-role assigned, can usually update the related business-obj
The admin-role also comprises lesser roles, through which the SELECT-permission is granted.
-#### agent
+#### AGENT
The agent-role is not used in the examples of this document, because it's for more complex cases.
It's usually granted to those roles and users who represent the related business-object, but are not allowed to update it.
@@ -231,21 +231,25 @@ Other than the tenant-role, it usually offers broader visibility of sub-business
E.g. a package-admin is allowed to see the related debitor-business-object,
but not its banking data.
-#### tenant
+#### TENANT
The tenant-role is granted to everybody who needs to be able to select the business-object and (probably some) related business-objects.
Usually all owners, admins and tenants of sub-objects get this role granted.
Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get SELECT permission.
-#### guest
+#### GUEST
+
+(Deprecated)
+
+#### REFERRER
Like the agent-role, the guest-role too is not used in the examples of this document, because it's for more complex cases.
-If the guest-role exists, the SELECT-permission is granted to it, instead of to the tenant-role.
-Other than the tenant-role, the guest-roles does never grant any roles of related objects.
+If the referrer-role exists, the SELECT-permission is granted to it, instead of to the tenant-role.
+Other than the tenant-role, the referrer-roles does never grant any roles of related objects.
-Also, if the guest-role exists, the tenant-role receives the SELECT-permission through the guest-role.
+Also, if the referrer-role exists, the tenant-role receives the SELECT-permission through the referrer-role.
### Referenced Business Objects and Role-Depreciation
@@ -372,7 +376,7 @@ That user is also used for historicization and audit log, but which is a differe
If the session variable `hsadminng.assumedRoles` is set to a non-empty value, its content is interpreted as a list of semicolon-separated role names.
Example:
- SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin';
+ SET LOCAL hsadminng.assumedRoles = 'customer#aab:admin;customer#aac:admin';
In this case, not the current user but the assumed roles are used as a starting point for any further queries.
Roles which are not granted to the current user, directly or indirectly, cannot be assumed.
@@ -385,7 +389,7 @@ A full example is shown here:
BEGIN TRANSACTION;
SET SESSION SESSION AUTHORIZATION restricted;
SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';
- SET LOCAL hsadminng.assumedRoles = 'customer#aab.admin;customer#aac.admin';
+ SET LOCAL hsadminng.assumedRoles = 'customer#aab:admin;customer#aac:admin';
SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address"
FROM emailaddress_rv ema
@@ -466,14 +470,14 @@ together {
permCustomerXyzSELECT--> boCustXyz
}
-entity "Role customer#xyz.tenant" as roleCustXyzTenant
+entity "Role customer#xyz:TENANT" as roleCustXyzTenant
roleCustXyzTenant --> permCustomerXyzSELECT
-entity "Role customer#xyz.admin" as roleCustXyzAdmin
+entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
roleCustXyzAdmin --> roleCustXyzTenant
roleCustXyzAdmin --> permCustomerXyzINSERT:package
-entity "Role customer#xyz.owner" as roleCustXyzOwner
+entity "Role customer#xyz:OWNER" as roleCustXyzOwner
roleCustXyzOwner ..> roleCustXyzAdmin
roleCustXyzOwner --> permCustomerXyzDELETE
@@ -489,7 +493,7 @@ actorHostmaster --> roleAdmins
```
As you can see, there something special:
-From the 'Role customer#xyz.owner' to the 'Role customer#xyz.admin' there is a dashed line, whereas all other lines are solid lines.
+From the 'Role customer#xyz:OWNER' to the 'Role customer#xyz:admin' there is a dashed line, whereas all other lines are solid lines.
Solid lines means, that one role is granted to another and automatically assumed in all queries to the restricted views.
The dashed line means that one role is granted to another but not automatically assumed in queries to the restricted views.
@@ -537,15 +541,15 @@ together {
}
package {
- entity "Role customer#xyz.tenant" as roleCustXyzTenant
- entity "Role customer#xyz.admin" as roleCustXyzAdmin
- entity "Role customer#xyz.owner" as roleCustXyzOwner
+ entity "Role customer#xyz:TENANT" as roleCustXyzTenant
+ entity "Role customer#xyz:ADMIN" as roleCustXyzAdmin
+ entity "Role customer#xyz:OWNER" as roleCustXyzOwner
}
package {
- entity "Role package#xyz00.owner" as rolePacXyz00Owner
- entity "Role package#xyz00.admin" as rolePacXyz00Admin
- entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
+ entity "Role package#xyz00:OWNER" as rolePacXyz00Owner
+ entity "Role package#xyz00:ADMIN" as rolePacXyz00Admin
+ entity "Role package#xyz00:TENANT" as rolePacXyz00Tenant
}
rolePacXyz00Tenant --> permPacXyz00SELECT
diff --git a/sql/rbac-tests.sql b/sql/rbac-tests.sql
index e30ac926..351d1509 100644
--- a/sql/rbac-tests.sql
+++ b/sql/rbac-tests.sql
@@ -3,10 +3,10 @@
-- --------------------------------------------------------
-select isGranted(findRoleId('administrators'), findRoleId('test_package#aaa00.owner'));
-select isGranted(findRoleId('test_package#aaa00.owner'), findRoleId('administrators'));
--- call grantRoleToRole(findRoleId('test_package#aaa00.owner'), findRoleId('administrators'));
--- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00.owner'));
+select isGranted(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
+select isGranted(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
+-- call grantRoleToRole(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
+-- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select count(*)
FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacUser('superuser-fran@hostsharing.net'),
diff --git a/sql/rbac-view-option-experiments.sql b/sql/rbac-view-option-experiments.sql
index f6e80e10..c5c04487 100644
--- a/sql/rbac-view-option-experiments.sql
+++ b/sql/rbac-view-option-experiments.sql
@@ -83,7 +83,7 @@ select rr.uuid, rr.type from RbacGrants g
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'))
where objectTable='test_customer');
-call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
+call grantRoleToUser(findRoleId('test_customer#aaa:ADMIN'), findRbacUser('aaaaouq@example.com'));
select queryAllPermissionsOfSubjectId(findRbacUser('aaaaouq@example.com'));
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index b38d92b9..f1f8ffff 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -24,7 +24,10 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
-import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index 7bb4aea3..9a120ea3 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -68,7 +68,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
public static RbacView rbac() {
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
.withIdentityView(SQL.query("""
- SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as 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
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
index a9a72160..7ef34252 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/InsertTriggerGenerator.java
@@ -120,7 +120,7 @@ public class InsertTriggerGenerator {
}
},
() -> {
- System.err.println("WARNING: no explicit INSERT grant for " + rbacDef.getRootEntityAlias().simpleName() + " => implicitly grant INSERT to global.admin");
+ System.err.println("WARNING: no explicit INSERT grant for " + rbacDef.getRootEntityAlias().simpleName() + " => implicitly grant INSERT to global:ADMIN");
generateInsertPermissionTriggerAllowOnlyGlobalAdmin(plPgSql);
});
}
@@ -246,7 +246,7 @@ public class InsertTriggerGenerator {
}
private static String toVar(final RbacView.RbacRoleDefinition roleDef) {
- return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
+ return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().name());
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
index d6fe2ab3..6bba2b12 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
@@ -113,7 +113,7 @@ public class RbacView {
* An identity view is a view which maps an objectUuid to an idName.
* The idName should be a human-readable representation of the row, but as short as possible.
* The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'.
- * It's used to create the object-specific-role-names like test_customer#abc.admin - here 'abc' is the idName.
+ * It's used to create the object-specific-role-names like test_customer#abc:ADMIN - here 'abc' is the idName.
* The idName not necessarily unique in a table, but it should be avoided.
*
*
@@ -882,15 +882,12 @@ public class RbacView {
TENANT,
REFERRER,
+ @Deprecated
GUEST;
@Override
public String toString() {
- return ":" + roleName();
- }
-
- String roleName() {
- return name().toLowerCase();
+ return ":" + name();
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
index d6a9bc28..c6e775c9 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacViewMermaidFlowchartGenerator.java
@@ -48,7 +48,7 @@ public class RbacViewMermaidFlowchartGenerator {
flowchart.indented( () -> {
rbacDef.getEntityAliases().values().stream()
- .filter(e -> e.aliasName().startsWith(entity.aliasName() + "."))
+ .filter(e -> e.aliasName().startsWith(entity.aliasName() + ":"))
.forEach(this::renderEntitySubgraph);
wrapOutputInSubgraph(entity.aliasName() + ":roles", color,
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
index 719c8ab4..484415f2 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java
@@ -333,7 +333,7 @@ class RolesGrantsAndPermissionsGenerator {
return "globalAdmin()";
}
final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias());
- return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().roleName())
+ return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().name())
+ "(" + entityRefVar + ")";
}
@@ -359,7 +359,7 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.indented(() -> {
plPgSql.writeLn("${simpleVarName)${roleSuffix}(NEW),"
.replace("${simpleVarName)", simpleEntityVarName)
- .replace("${roleSuffix}", capitalize(role.roleName())));
+ .replace("${roleSuffix}", capitalize(role.name())));
generatePermissionsForRole(plPgSql, role);
@@ -562,7 +562,7 @@ class RolesGrantsAndPermissionsGenerator {
}
private static String toRoleRef(final RbacView.RbacRoleDefinition roleDef) {
- return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().roleName());
+ return uncapitalize(roleDef.getEntityAlias().simpleName()) + capitalize(roleDef.getRole().name());
}
private static String toTriggerReference(
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java
index a3abf528..c2f2d524 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntity.java
@@ -59,9 +59,9 @@ public class RbacGrantEntity {
}
public String toDisplay() {
- return "{ grant role " + grantedRoleIdName +
- " to user " + granteeUserName +
- " by role " + grantedByRoleIdName +
+ return "{ grant role:" + grantedRoleIdName +
+ " to user:" + granteeUserName +
+ " by role:" + grantedByRoleIdName +
(assumed ? " and assume" : "") +
" }";
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
index cf05496a..f8746eb5 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramService.java
@@ -71,14 +71,14 @@ public class RbacGrantsDiagramService {
private void traverseGrantsTo(final Set graph, final UUID refUuid, final EnumSet includes) {
final var grants = rawGrantRepo.findByAscendingUuid(refUuid);
grants.forEach(g -> {
- if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
+ if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm:")) {
return;
}
- if ( !g.getDescendantIdName().startsWith("role global")) {
- if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
+ if ( !g.getDescendantIdName().startsWith("role:global")) {
+ if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(":test_")) {
return;
}
- if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
+ if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(":test_")) {
return;
}
}
@@ -102,7 +102,7 @@ public class RbacGrantsDiagramService {
private void traverseGrantsFrom(final Set graph, final UUID refUuid, final EnumSet option) {
final var grants = rawGrantRepo.findByDescendantUuid(refUuid);
grants.forEach(g -> {
- if (!option.contains(USERS) && g.getAscendantIdName().startsWith("user ")) {
+ if (!option.contains(USERS) && g.getAscendantIdName().startsWith("user:")) {
return;
}
graph.add(g);
@@ -171,7 +171,7 @@ public class RbacGrantsDiagramService {
}
if (refType.equals("role")) {
final var withoutRolePrefix = node.idName().substring("role:".length());
- return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf('.'));
+ return withoutRolePrefix.substring(0, withoutRolePrefix.lastIndexOf(':'));
}
throw new IllegalArgumentException("unknown refType '" + refType + "' in '" + node.idName() + "'");
}
@@ -188,23 +188,23 @@ public class RbacGrantsDiagramService {
return "(" + displayName + "\nref:" + uuid + ")";
}
if (refType.equals("role")) {
- final var roleType = idName.substring(idName.lastIndexOf('.') + 1);
+ final var roleType = idName.substring(idName.lastIndexOf(':') + 1);
return "[" + roleType + "\nref:" + uuid + "]";
}
if (refType.equals("perm")) {
- final var roleType = idName.split(" ")[1];
+ final var roleType = idName.split(":")[1];
return "{{" + roleType + "\nref:" + uuid + "}}";
}
return "";
}
private static String refType(final String idName) {
- return idName.split(" ", 2)[0];
+ return idName.split(":", 2)[0];
}
@NotNull
private static String cleanId(final String idName) {
- return idName.replace(" ", ":").replaceAll("@.*", "")
+ return idName.replaceAll("@.*", "")
.replace("[", "").replace("]", "").replace("(", "").replace(")", "").replace(",", "");
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java
index 26528c8a..fa21785a 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleEntity.java
@@ -34,6 +34,6 @@ public class RbacRoleEntity {
@Enumerated(EnumType.STRING)
private RbacRoleType roleType;
- @Formula("objectTable||'#'||objectIdName||'.'||roleType")
+ @Formula("objectTable||'#'||objectIdName||':'||roleType")
private String roleName;
}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
index fa5b16aa..e78e8836 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleType.java
@@ -1,5 +1,5 @@
package net.hostsharing.hsadminng.rbac.rbacrole;
public enum RbacRoleType {
- owner, admin, agent, tenant, guest, referrer
+ OWNER, ADMIN, AGENT, TENANT, GUEST, REFERRER
}
diff --git a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
index ff0e18e4..45736dc3 100644
--- a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
+++ b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
@@ -19,9 +19,11 @@ components:
roleType:
type: string
enum:
- - owner
- - admin
- - tenant
- - referrer
+ - OWNER
+ - ADMIN
+ - AGENT
+ - TENANT
+ - GUEST
+ - REFERRER
roleName:
type: string
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql
index ba655e93..3bb37037 100644
--- a/src/main/resources/db/changelog/010-context.sql
+++ b/src/main/resources/db/changelog/010-context.sql
@@ -149,8 +149,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
declare
cleanIdentifier varchar;
begin
- -- TODO: remove the ':' from the list of allowed characters as soon as it's not used anymore
- cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
+ cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._]+', '', 'g');
return cleanIdentifier;
end; $$;
diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql
index ca560bf9..6a3387fb 100644
--- a/src/main/resources/db/changelog/050-rbac-base.sql
+++ b/src/main/resources/db/changelog/050-rbac-base.sql
@@ -164,7 +164,7 @@ end; $$;
*/
-create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest', 'referrer');
+create type RbacRoleType as enum ('OWNER', 'ADMIN', 'AGENT', 'TENANT', 'GUEST', 'REFERRER');
create table RbacRole
(
@@ -249,7 +249,7 @@ declare
roleUuid uuid;
begin
-- TODO.refact: extract function toRbacRoleDescriptor(roleIdName varchar) + find other occurrences
- roleParts = overlay(roleIdName placing '#' from length(roleIdName) + 1 - strpos(reverse(roleIdName), '.'));
+ roleParts = overlay(roleIdName placing '#' from length(roleIdName) + 1 - strpos(reverse(roleIdName), ':'));
objectTableFromRoleIdName = split_part(roleParts, '#', 1);
objectNameFromRoleIdName = split_part(roleParts, '#', 2);
roleTypeFromRoleIdName = split_part(roleParts, '#', 3);
diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/054-rbac-context.sql
index 5437131f..faae1782 100644
--- a/src/main/resources/db/changelog/054-rbac-context.sql
+++ b/src/main/resources/db/changelog/054-rbac-context.sql
@@ -50,7 +50,7 @@ begin
foreach roleName in array string_to_array(assumedRoles, ';')
loop
- roleNameParts = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.'));
+ roleNameParts = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), ':'));
objectTableToAssume = split_part(roleNameParts, '#', 1);
objectNameToAssume = split_part(roleNameParts, '#', 2);
roleTypeToAssume = split_part(roleNameParts, '#', 3);
diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql
index 408c3594..a8570f6c 100644
--- a/src/main/resources/db/changelog/055-rbac-views.sql
+++ b/src/main/resources/db/changelog/055-rbac-views.sql
@@ -9,7 +9,7 @@
*/
drop view if exists rbacrole_ev;
create or replace view rbacrole_ev as
-select (objectTable || '#' || objectIdName || '.' || roleType) as roleIdName, *
+select (objectTable || '#' || objectIdName || ':' || roleType) as roleIdName, *
-- @formatter:off
from (
select r.*,
@@ -40,7 +40,7 @@ select *
where isGranted(currentSubjectsUuids(), r.uuid)
) as unordered
-- @formatter:on
- order by objectTable || '#' || objectIdName || '.' || roleType;
+ order by objectTable || '#' || objectIdName || ':' || roleType;
grant all privileges on rbacrole_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
--//
@@ -57,7 +57,7 @@ create or replace view rbacgrants_ev as
-- @formatter:off
select x.grantUuid as uuid,
x.grantedByTriggerOf as grantedByTriggerOf,
- go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || '.' || r.roletype as grantedByRoleIdName,
+ go.objectTable || '#' || findIdNameByObjectUuid(go.objectTable, go.uuid) || ':' || r.roletype as grantedByRoleIdName,
x.ascendingIdName as ascendantIdName,
x.descendingIdName as descendantIdName,
x.grantedByRoleUuid,
@@ -71,16 +71,16 @@ create or replace view rbacgrants_ev as
g.grantedbyroleuuid, g.ascendantuuid, g.descendantuuid, g.assumed,
coalesce(
- 'user ' || au.name,
- 'role ' || aro.objectTable || '#' || findIdNameByObjectUuid(aro.objectTable, aro.uuid) || '.' || ar.roletype
+ 'user:' || au.name,
+ 'role:' || aro.objectTable || '#' || findIdNameByObjectUuid(aro.objectTable, aro.uuid) || ':' || ar.roletype
) as ascendingIdName,
aro.objectTable, aro.uuid,
( case
when dro is not null
- then ('role ' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || '.' || dr.roletype)
+ then ('role:' || dro.objectTable || '#' || findIdNameByObjectUuid(dro.objectTable, dro.uuid) || ':' || dr.roletype)
when dp.op = 'INSERT'
- then 'perm ' || dp.op || ' into ' || dp.opTableName || ' with ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
- else 'perm ' || dp.op || ' on ' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid)
+ then 'perm:' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid) || ':' || dp.op || '>' || dp.opTableName
+ else 'perm:' || dpo.objecttable || '#' || findIdNameByObjectUuid(dpo.objectTable, dpo.uuid) || ':' || dp.op
end
) as descendingIdName,
dro.objectTable, dro.uuid,
@@ -115,8 +115,8 @@ create or replace view rbacgrants_ev as
drop view if exists rbacgrants_rv;
create or replace view rbacgrants_rv as
-- @formatter:off
-select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || '.' || r.roletype as grantedByRoleIdName,
- g.objectTable || '#' || g.objectIdName || '.' || g.roletype as grantedRoleIdName, g.userName, g.assumed,
+select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || ':' || r.roletype as grantedByRoleIdName,
+ g.objectTable || '#' || g.objectIdName || ':' || g.roletype as grantedRoleIdName, g.userName, g.assumed,
g.grantedByRoleUuid, g.descendantUuid as grantedRoleUuid, g.ascendantUuid as userUuid,
g.objectTable, g.objectUuid, g.objectIdName, g.roleType as grantedRoleType
from (
@@ -327,7 +327,7 @@ execute function deleteRbacUser();
drop view if exists RbacOwnGrantedPermissions_rv;
create or replace view RbacOwnGrantedPermissions_rv as
select r.uuid as roleuuid, p.uuid as permissionUuid,
- (r.objecttable || '#' || r.objectidname || '.' || r.roletype) as roleName, p.op,
+ (r.objecttable || ':' || r.objectidname || ':' || r.roletype) as roleName, p.op,
o.objecttable, r.objectidname, o.uuid as objectuuid
from rbacrole_rv r
join rbacgrants g on g.ascendantuuid = r.uuid
@@ -359,7 +359,7 @@ begin
return query select
xp.roleUuid,
- (xp.roleObjectTable || '#' || xp.roleObjectIdName || '.' || xp.roleType) as roleName,
+ (xp.roleObjectTable || '#' || xp.roleObjectIdName || ':' || xp.roleType) as roleName,
xp.permissionUuid, xp.op, xp.opTableName,
xp.permissionObjectTable, xp.permissionObjectIdName, xp.permissionObjectUuid
from (select
diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql
index efe71b1b..958d3afe 100644
--- a/src/main/resources/db/changelog/058-rbac-generators.sql
+++ b/src/main/resources/db/changelog/058-rbac-generators.sql
@@ -46,7 +46,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'owner', assumed);
+ return roleDescriptor('%2$s', entity.uuid, 'OWNER', assumed);
end; $f$;
create or replace function %1$sAdmin(entity %2$s, assumed boolean = true)
@@ -54,7 +54,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'admin', assumed);
+ return roleDescriptor('%2$s', entity.uuid, 'ADMIN', assumed);
end; $f$;
create or replace function %1$sAgent(entity %2$s, assumed boolean = true)
@@ -62,7 +62,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'agent', assumed);
+ return roleDescriptor('%2$s', entity.uuid, 'AGENT', assumed);
end; $f$;
create or replace function %1$sTenant(entity %2$s, assumed boolean = true)
@@ -70,7 +70,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
+ return roleDescriptor('%2$s', entity.uuid, 'TENANT', assumed);
end; $f$;
-- TODO: remove guest role
@@ -79,7 +79,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
+ return roleDescriptor('%2$s', entity.uuid, 'GUEST', assumed);
end; $f$;
create or replace function %1$sReferrer(entity %2$s)
@@ -87,7 +87,7 @@ begin
language plpgsql
strict as $f$
begin
- return roleDescriptor('%2$s', entity.uuid, 'referrer');
+ return roleDescriptor('%2$s', entity.uuid, 'REFERRER');
end; $f$;
$sql$, prefix, targetTable);
diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql
index f8058113..3078922f 100644
--- a/src/main/resources/db/changelog/080-rbac-global.sql
+++ b/src/main/resources/db/changelog/080-rbac-global.sql
@@ -114,11 +114,11 @@ create or replace function globalAdmin(assumed boolean = true)
returns null on null input
stable -- leakproof
language sql as $$
-select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType, assumed;
+select 'global', (select uuid from RbacObject where objectTable = 'global'), 'ADMIN'::RbacRoleType, assumed;
$$;
begin transaction;
- call defineContext('creating global admin role', null, null, null);
+ call defineContext('creating role:global#global:ADMIN', null, null, null);
select createRole(globalAdmin());
commit;
--//
@@ -135,11 +135,11 @@ create or replace function globalGuest(assumed boolean = true)
returns null on null input
stable -- leakproof
language sql as $$
-select 'global', (select uuid from RbacObject where objectTable = 'global'), 'guest'::RbacRoleType, assumed;
+select 'global', (select uuid from RbacObject where objectTable = 'global'), 'GUEST'::RbacRoleType, assumed;
$$;
begin transaction;
- call defineContext('creating global guest role', null, null, null);
+ call defineContext('creating role:global#globa:guest', null, null, null);
select createRole(globalGuest());
commit;
--//
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.md b/src/main/resources/db/changelog/113-test-customer-rbac.md
index 4d63eeac..19e67a38 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.md
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.md
@@ -13,9 +13,9 @@ subgraph customer["`**customer**`"]
subgraph customer:roles[ ]
style customer:roles fill:#dd4901,stroke:white
- role:customer:owner[[customer:owner]]
- role:customer:admin[[customer:admin]]
- role:customer:tenant[[customer:tenant]]
+ role:customer:OWNER[[customer:OWNER]]
+ role:customer:ADMIN[[customer:ADMIN]]
+ role:customer:TENANT[[customer:TENANT]]
end
subgraph customer:permissions[ ]
@@ -29,17 +29,17 @@ subgraph customer["`**customer**`"]
end
%% granting roles to users
-user:creator ==>|XX| role:customer:owner
+user:creator ==>|XX| role:customer:OWNER
%% granting roles to roles
-role:global:admin ==>|XX| role:customer:owner
-role:customer:owner ==> role:customer:admin
-role:customer:admin ==> role:customer:tenant
+role:global:ADMIN ==>|XX| role:customer:OWNER
+role:customer:OWNER ==> role:customer:ADMIN
+role:customer:ADMIN ==> role:customer:TENANT
%% granting permissions to roles
-role:global:admin ==> perm:customer:INSERT
-role:customer:owner ==> perm:customer:DELETE
-role:customer:admin ==> perm:customer:UPDATE
-role:customer:tenant ==> perm:customer:SELECT
+role:global:ADMIN ==> perm:customer:INSERT
+role:customer:OWNER ==> perm:customer:DELETE
+role:customer:ADMIN ==> perm:customer:UPDATE
+role:customer:TENANT ==> perm:customer:SELECT
```
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/113-test-customer-rbac.sql
index fd460049..2f9ea4de 100644
--- a/src/main/resources/db/changelog/113-test-customer-rbac.sql
+++ b/src/main/resources/db/changelog/113-test-customer-rbac.sql
@@ -35,22 +35,22 @@ begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
- testCustomerOwner(NEW),
+ testCustomerOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin(unassumed())],
+ incomingSuperRoles => array[globalADMIN(unassumed())],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- testCustomerAdmin(NEW),
+ testCustomerADMIN(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[testCustomerOwner(NEW)]
+ incomingSuperRoles => array[testCustomerOWNER(NEW)]
);
perform createRoleWithGrants(
- testCustomerTenant(NEW),
+ testCustomerTENANT(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[testCustomerAdmin(NEW)]
+ incomingSuperRoles => array[testCustomerADMIN(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -93,7 +93,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'test_customer'),
- globalAdmin());
+ globalADMIN());
END LOOP;
END;
$$;
@@ -108,7 +108,7 @@ create or replace function test_customer_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'test_customer'),
- globalAdmin());
+ globalADMIN());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/118-test-customer-test-data.sql b/src/main/resources/db/changelog/118-test-customer-test-data.sql
index 85c34ac6..73c8e535 100644
--- a/src/main/resources/db/changelog/118-test-customer-test-data.sql
+++ b/src/main/resources/db/changelog/118-test-customer-test-data.sql
@@ -32,7 +32,7 @@ declare
newCust test_customer;
begin
currentTask = 'creating RBAC test customer #' || custReference || '/' || custPrefix;
- call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+ call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
execute format('set local hsadminng.currentTask to %L', currentTask);
custRowId = uuid_generate_v4();
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.md b/src/main/resources/db/changelog/123-test-package-rbac.md
index 34b8c7c7..368cfe2f 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.md
+++ b/src/main/resources/db/changelog/123-test-package-rbac.md
@@ -13,9 +13,9 @@ subgraph package["`**package**`"]
subgraph package:roles[ ]
style package:roles fill:#dd4901,stroke:white
- role:package:owner[[package:owner]]
- role:package:admin[[package:admin]]
- role:package:tenant[[package:tenant]]
+ role:package:OWNER[[package:OWNER]]
+ role:package:ADMIN[[package:ADMIN]]
+ role:package:TENANT[[package:TENANT]]
end
subgraph package:permissions[ ]
@@ -35,25 +35,25 @@ subgraph customer["`**customer**`"]
subgraph customer:roles[ ]
style customer:roles fill:#99bcdb,stroke:white
- role:customer:owner[[customer:owner]]
- role:customer:admin[[customer:admin]]
- role:customer:tenant[[customer:tenant]]
+ role:customer:OWNER[[customer:OWNER]]
+ role:customer:ADMIN[[customer:ADMIN]]
+ role:customer:TENANT[[customer:TENANT]]
end
end
%% granting roles to roles
-role:global:admin -.->|XX| role:customer:owner
-role:customer:owner -.-> role:customer:admin
-role:customer:admin -.-> role:customer:tenant
-role:customer:admin ==> role:package:owner
-role:package:owner ==> role:package:admin
-role:package:admin ==> role:package:tenant
-role:package:tenant ==> role:customer:tenant
+role:global:ADMIN -.->|XX| role:customer:OWNER
+role:customer:OWNER -.-> role:customer:ADMIN
+role:customer:ADMIN -.-> role:customer:TENANT
+role:customer:ADMIN ==> role:package:OWNER
+role:package:OWNER ==> role:package:ADMIN
+role:package:ADMIN ==> role:package:TENANT
+role:package:TENANT ==> role:customer:TENANT
%% granting permissions to roles
-role:customer:admin ==> perm:package:INSERT
-role:package:owner ==> perm:package:DELETE
-role:package:owner ==> perm:package:UPDATE
-role:package:tenant ==> perm:package:SELECT
+role:customer:ADMIN ==> perm:package:INSERT
+role:package:OWNER ==> perm:package:DELETE
+role:package:OWNER ==> perm:package:UPDATE
+role:package:TENANT ==> perm:package:SELECT
```
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql
index 972b174d..3a4d5d8b 100644
--- a/src/main/resources/db/changelog/123-test-package-rbac.sql
+++ b/src/main/resources/db/changelog/123-test-package-rbac.sql
@@ -40,21 +40,21 @@ begin
perform createRoleWithGrants(
- testPackageOwner(NEW),
+ testPackageOWNER(NEW),
permissions => array['DELETE', 'UPDATE'],
- incomingSuperRoles => array[testCustomerAdmin(newCustomer)]
+ incomingSuperRoles => array[testCustomerADMIN(newCustomer)]
);
perform createRoleWithGrants(
- testPackageAdmin(NEW),
- incomingSuperRoles => array[testPackageOwner(NEW)]
+ testPackageADMIN(NEW),
+ incomingSuperRoles => array[testPackageOWNER(NEW)]
);
perform createRoleWithGrants(
- testPackageTenant(NEW),
+ testPackageTENANT(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[testPackageAdmin(NEW)],
- outgoingSubRoles => array[testCustomerTenant(newCustomer)]
+ incomingSuperRoles => array[testPackageADMIN(NEW)],
+ outgoingSubRoles => array[testCustomerTENANT(newCustomer)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -110,11 +110,11 @@ begin
if NEW.customerUuid <> OLD.customerUuid then
- call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
- call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
+ call revokeRoleFromRole(testPackageOWNER(OLD), testCustomerADMIN(oldCustomer));
+ call grantRoleToRole(testPackageOWNER(NEW), testCustomerADMIN(newCustomer));
- call revokeRoleFromRole(testCustomerTenant(oldCustomer), testPackageTenant(OLD));
- call grantRoleToRole(testCustomerTenant(newCustomer), testPackageTenant(NEW));
+ call revokeRoleFromRole(testCustomerTENANT(oldCustomer), testPackageTENANT(OLD));
+ call grantRoleToRole(testCustomerTENANT(newCustomer), testPackageTENANT(NEW));
end if;
@@ -158,7 +158,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'test_package'),
- testCustomerAdmin(row));
+ testCustomerADMIN(row));
END LOOP;
END;
$$;
@@ -173,7 +173,7 @@ create or replace function test_package_test_customer_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'test_package'),
- testCustomerAdmin(NEW));
+ testCustomerADMIN(NEW));
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/128-test-package-test-data.sql b/src/main/resources/db/changelog/128-test-package-test-data.sql
index 9abba772..f50ad480 100644
--- a/src/main/resources/db/changelog/128-test-package-test-data.sql
+++ b/src/main/resources/db/changelog/128-test-package-test-data.sql
@@ -25,7 +25,7 @@ begin
cust.uuid;
custAdminUser = 'customer-admin@' || cust.prefix || '.example.com';
- custAdminRole = 'test_customer#' || cust.prefix || '.admin';
+ custAdminRole = 'test_customer#' || cust.prefix || ':ADMIN';
call defineContext(currentTask, null, 'superuser-fran@hostsharing.net', custAdminRole);
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.md b/src/main/resources/db/changelog/133-test-domain-rbac.md
index 6954e9b8..d9b3748c 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.md
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.md
@@ -13,9 +13,9 @@ subgraph package.customer["`**package.customer**`"]
subgraph package.customer:roles[ ]
style package.customer:roles fill:#99bcdb,stroke:white
- role:package.customer:owner[[package.customer:owner]]
- role:package.customer:admin[[package.customer:admin]]
- role:package.customer:tenant[[package.customer:tenant]]
+ role:package.customer:OWNER[[package.customer:OWNER]]
+ role:package.customer:ADMIN[[package.customer:ADMIN]]
+ role:package.customer:TENANT[[package.customer:TENANT]]
end
end
@@ -23,25 +23,12 @@ subgraph package["`**package**`"]
direction TB
style package fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- subgraph package.customer["`**package.customer**`"]
- direction TB
- style package.customer fill:#99bcdb,stroke:#274d6e,stroke-width:8px
-
- subgraph package.customer:roles[ ]
- style package.customer:roles fill:#99bcdb,stroke:white
-
- role:package.customer:owner[[package.customer:owner]]
- role:package.customer:admin[[package.customer:admin]]
- role:package.customer:tenant[[package.customer:tenant]]
- end
- end
-
subgraph package:roles[ ]
style package:roles fill:#99bcdb,stroke:white
- role:package:owner[[package:owner]]
- role:package:admin[[package:admin]]
- role:package:tenant[[package:tenant]]
+ role:package:OWNER[[package:OWNER]]
+ role:package:ADMIN[[package:ADMIN]]
+ role:package:TENANT[[package:TENANT]]
end
end
@@ -52,8 +39,8 @@ subgraph domain["`**domain**`"]
subgraph domain:roles[ ]
style domain:roles fill:#dd4901,stroke:white
- role:domain:owner[[domain:owner]]
- role:domain:admin[[domain:admin]]
+ role:domain:OWNER[[domain:OWNER]]
+ role:domain:ADMIN[[domain:ADMIN]]
end
subgraph domain:permissions[ ]
@@ -67,22 +54,22 @@ subgraph domain["`**domain**`"]
end
%% granting roles to roles
-role:global:admin -.->|XX| role:package.customer:owner
-role:package.customer:owner -.-> role:package.customer:admin
-role:package.customer:admin -.-> role:package.customer:tenant
-role:package.customer:admin -.-> role:package:owner
-role:package:owner -.-> role:package:admin
-role:package:admin -.-> role:package:tenant
-role:package:tenant -.-> role:package.customer:tenant
-role:package:admin ==> role:domain:owner
-role:domain:owner ==> role:package:tenant
-role:domain:owner ==> role:domain:admin
-role:domain:admin ==> role:package:tenant
+role:global:ADMIN -.->|XX| role:package.customer:OWNER
+role:package.customer:OWNER -.-> role:package.customer:ADMIN
+role:package.customer:ADMIN -.-> role:package.customer:TENANT
+role:package.customer:ADMIN -.-> role:package:OWNER
+role:package:OWNER -.-> role:package:ADMIN
+role:package:ADMIN -.-> role:package:TENANT
+role:package:TENANT -.-> role:package.customer:TENANT
+role:package:ADMIN ==> role:domain:OWNER
+role:domain:OWNER ==> role:package:TENANT
+role:domain:OWNER ==> role:domain:ADMIN
+role:domain:ADMIN ==> role:package:TENANT
%% granting permissions to roles
-role:package:admin ==> perm:domain:INSERT
-role:domain:owner ==> perm:domain:DELETE
-role:domain:owner ==> perm:domain:UPDATE
-role:domain:admin ==> perm:domain:SELECT
+role:package:ADMIN ==> perm:domain:INSERT
+role:domain:OWNER ==> perm:domain:DELETE
+role:domain:OWNER ==> perm:domain:UPDATE
+role:domain:ADMIN ==> perm:domain:SELECT
```
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql
index 7a891841..de5faa78 100644
--- a/src/main/resources/db/changelog/133-test-domain-rbac.sql
+++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql
@@ -40,17 +40,17 @@ begin
perform createRoleWithGrants(
- testDomainOwner(NEW),
+ testDomainOWNER(NEW),
permissions => array['DELETE', 'UPDATE'],
- incomingSuperRoles => array[testPackageAdmin(newPackage)],
- outgoingSubRoles => array[testPackageTenant(newPackage)]
+ incomingSuperRoles => array[testPackageADMIN(newPackage)],
+ outgoingSubRoles => array[testPackageTENANT(newPackage)]
);
perform createRoleWithGrants(
- testDomainAdmin(NEW),
+ testDomainADMIN(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[testDomainOwner(NEW)],
- outgoingSubRoles => array[testPackageTenant(newPackage)]
+ incomingSuperRoles => array[testDomainOWNER(NEW)],
+ outgoingSubRoles => array[testPackageTENANT(newPackage)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -106,14 +106,14 @@ begin
if NEW.packageUuid <> OLD.packageUuid then
- call revokeRoleFromRole(testDomainOwner(OLD), testPackageAdmin(oldPackage));
- call grantRoleToRole(testDomainOwner(NEW), testPackageAdmin(newPackage));
+ call revokeRoleFromRole(testDomainOWNER(OLD), testPackageADMIN(oldPackage));
+ call grantRoleToRole(testDomainOWNER(NEW), testPackageADMIN(newPackage));
- call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainOwner(OLD));
- call grantRoleToRole(testPackageTenant(newPackage), testDomainOwner(NEW));
+ call revokeRoleFromRole(testPackageTENANT(oldPackage), testDomainOWNER(OLD));
+ call grantRoleToRole(testPackageTENANT(newPackage), testDomainOWNER(NEW));
- call revokeRoleFromRole(testPackageTenant(oldPackage), testDomainAdmin(OLD));
- call grantRoleToRole(testPackageTenant(newPackage), testDomainAdmin(NEW));
+ call revokeRoleFromRole(testPackageTENANT(oldPackage), testDomainADMIN(OLD));
+ call grantRoleToRole(testPackageTENANT(newPackage), testDomainADMIN(NEW));
end if;
@@ -157,7 +157,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'test_domain'),
- testPackageAdmin(row));
+ testPackageADMIN(row));
END LOOP;
END;
$$;
@@ -172,7 +172,7 @@ create or replace function test_domain_test_package_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'test_domain'),
- testPackageAdmin(NEW));
+ testPackageADMIN(NEW));
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.md b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md
index 52584907..fe736072 100644
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.md
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.md
@@ -13,9 +13,9 @@ subgraph contact["`**contact**`"]
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]]
+ role:contact:OWNER[[contact:OWNER]]
+ role:contact:ADMIN[[contact:ADMIN]]
+ role:contact:REFERRER[[contact:REFERRER]]
end
subgraph contact:permissions[ ]
@@ -29,17 +29,17 @@ subgraph contact["`**contact**`"]
end
%% granting roles to users
-user:creator ==> role:contact:owner
+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
+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
+role:contact:OWNER ==> perm:contact:DELETE
+role:contact:ADMIN ==> perm:contact:UPDATE
+role:contact:REFERRER ==> perm:contact:SELECT
+role:global:GUEST ==> perm:contact:INSERT
```
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
index 0e08e15f..0f53b167 100644
--- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
+++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
@@ -35,22 +35,22 @@ begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
- hsOfficeContactOwner(NEW),
+ hsOfficeContactOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
+ incomingSuperRoles => array[globalADMIN()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficeContactAdmin(NEW),
+ hsOfficeContactADMIN(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
+ incomingSuperRoles => array[hsOfficeContactOWNER(NEW)]
);
perform createRoleWithGrants(
- hsOfficeContactReferrer(NEW),
+ hsOfficeContactREFERRER(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
+ incomingSuperRoles => array[hsOfficeContactADMIN(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -93,7 +93,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_contact'),
- globalGuest());
+ globalGUEST());
END LOOP;
END;
$$;
@@ -108,7 +108,7 @@ create or replace function hs_office_contact_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_contact'),
- globalGuest());
+ globalGUEST());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.md b/src/main/resources/db/changelog/213-hs-office-person-rbac.md
index 70e0f33a..d0eebfdd 100644
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac.md
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.md
@@ -13,9 +13,9 @@ subgraph person["`**person**`"]
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]]
+ role:person:OWNER[[person:OWNER]]
+ role:person:ADMIN[[person:ADMIN]]
+ role:person:REFERRER[[person:REFERRER]]
end
subgraph person:permissions[ ]
@@ -29,17 +29,17 @@ subgraph person["`**person**`"]
end
%% granting roles to users
-user:creator ==> role:person:owner
+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
+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
+role:global:GUEST ==> perm:person:INSERT
+role:person:OWNER ==> perm:person:DELETE
+role:person:ADMIN ==> perm:person:UPDATE
+role:person:REFERRER ==> perm:person:SELECT
```
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
index adbdae33..6dbbf21b 100644
--- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
+++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql
@@ -35,22 +35,22 @@ begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
- hsOfficePersonOwner(NEW),
+ hsOfficePersonOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
+ incomingSuperRoles => array[globalADMIN()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficePersonAdmin(NEW),
+ hsOfficePersonADMIN(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
+ incomingSuperRoles => array[hsOfficePersonOWNER(NEW)]
);
perform createRoleWithGrants(
- hsOfficePersonReferrer(NEW),
+ hsOfficePersonREFERRER(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
+ incomingSuperRoles => array[hsOfficePersonADMIN(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -93,7 +93,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_person'),
- globalGuest());
+ globalGUEST());
END LOOP;
END;
$$;
@@ -108,7 +108,7 @@ create or replace function hs_office_person_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_person'),
- globalGuest());
+ globalGUEST());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
index 8e5524ec..8014cdaf 100644
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.md
@@ -13,9 +13,9 @@ subgraph holderPerson["`**holderPerson**`"]
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]]
+ role:holderPerson:OWNER[[holderPerson:OWNER]]
+ role:holderPerson:ADMIN[[holderPerson:ADMIN]]
+ role:holderPerson:REFERRER[[holderPerson:REFERRER]]
end
end
@@ -26,9 +26,9 @@ subgraph anchorPerson["`**anchorPerson**`"]
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]]
+ role:anchorPerson:OWNER[[anchorPerson:OWNER]]
+ role:anchorPerson:ADMIN[[anchorPerson:ADMIN]]
+ role:anchorPerson:REFERRER[[anchorPerson:REFERRER]]
end
end
@@ -39,9 +39,9 @@ subgraph contact["`**contact**`"]
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]]
+ role:contact:OWNER[[contact:OWNER]]
+ role:contact:ADMIN[[contact:ADMIN]]
+ role:contact:REFERRER[[contact:REFERRER]]
end
end
@@ -52,10 +52,10 @@ subgraph relation["`**relation**`"]
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]]
+ role:relation:OWNER[[relation:OWNER]]
+ role:relation:ADMIN[[relation:ADMIN]]
+ role:relation:AGENT[[relation:AGENT]]
+ role:relation:TENANT[[relation:TENANT]]
end
subgraph relation:permissions[ ]
@@ -69,34 +69,34 @@ subgraph relation["`**relation**`"]
end
%% granting roles to users
-user:creator ==> role:relation:owner
+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
+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
+role:relation:OWNER ==> perm:relation:DELETE
+role:relation:ADMIN ==> perm:relation:UPDATE
+role:relation:TENANT ==> perm:relation:SELECT
+role:anchorPerson:ADMIN ==> perm:relation:INSERT
```
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
index 6c9ae616..ff890a59 100644
--- a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
+++ b/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
@@ -48,38 +48,38 @@ begin
perform createRoleWithGrants(
- hsOfficeRelationOwner(NEW),
+ hsOfficeRelationOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
+ incomingSuperRoles => array[globalADMIN()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficeRelationAdmin(NEW),
+ hsOfficeRelationADMIN(NEW),
permissions => array['UPDATE'],
incomingSuperRoles => array[
- hsOfficePersonAdmin(newAnchorPerson),
- hsOfficeRelationOwner(NEW)]
+ hsOfficePersonADMIN(newAnchorPerson),
+ hsOfficeRelationOWNER(NEW)]
);
perform createRoleWithGrants(
- hsOfficeRelationAgent(NEW),
+ hsOfficeRelationAGENT(NEW),
incomingSuperRoles => array[
- hsOfficePersonAdmin(newHolderPerson),
- hsOfficeRelationAdmin(NEW)]
+ hsOfficePersonADMIN(newHolderPerson),
+ hsOfficeRelationADMIN(NEW)]
);
perform createRoleWithGrants(
- hsOfficeRelationTenant(NEW),
+ hsOfficeRelationTENANT(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
- hsOfficeContactAdmin(newContact),
- hsOfficePersonAdmin(newHolderPerson),
- hsOfficeRelationAgent(NEW)],
+ hsOfficeContactADMIN(newContact),
+ hsOfficePersonADMIN(newHolderPerson),
+ hsOfficeRelationAGENT(NEW)],
outgoingSubRoles => array[
- hsOfficeContactReferrer(newContact),
- hsOfficePersonReferrer(newAnchorPerson),
- hsOfficePersonReferrer(newHolderPerson)]
+ hsOfficeContactREFERRER(newContact),
+ hsOfficePersonREFERRER(newAnchorPerson),
+ hsOfficePersonREFERRER(newHolderPerson)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -151,11 +151,11 @@ begin
if NEW.contactUuid <> OLD.contactUuid then
- call revokeRoleFromRole(hsOfficeRelationTenant(OLD), hsOfficeContactAdmin(oldContact));
- call grantRoleToRole(hsOfficeRelationTenant(NEW), hsOfficeContactAdmin(newContact));
+ call revokeRoleFromRole(hsOfficeRelationTENANT(OLD), hsOfficeContactADMIN(oldContact));
+ call grantRoleToRole(hsOfficeRelationTENANT(NEW), hsOfficeContactADMIN(newContact));
- call revokeRoleFromRole(hsOfficeContactReferrer(oldContact), hsOfficeRelationTenant(OLD));
- call grantRoleToRole(hsOfficeContactReferrer(newContact), hsOfficeRelationTenant(NEW));
+ call revokeRoleFromRole(hsOfficeContactREFERRER(oldContact), hsOfficeRelationTENANT(OLD));
+ call grantRoleToRole(hsOfficeContactREFERRER(newContact), hsOfficeRelationTENANT(NEW));
end if;
@@ -199,7 +199,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_relation'),
- hsOfficePersonAdmin(row));
+ hsOfficePersonADMIN(row));
END LOOP;
END;
$$;
@@ -214,7 +214,7 @@ create or replace function hs_office_relation_hs_office_person_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_relation'),
- hsOfficePersonAdmin(NEW));
+ hsOfficePersonADMIN(NEW));
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
index 9bdcab18..61691d6f 100644
--- a/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
+++ b/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
@@ -25,7 +25,7 @@ declare
begin
idName := cleanIdentifier( anchorPersonName || '-' || holderPersonName);
currentTask := 'creating relation test-data ' || idName;
- call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+ call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
execute format('set local hsadminng.currentTask to %L', currentTask);
select p.*
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
index 98bd276d..a0caa074 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.md
@@ -13,9 +13,9 @@ subgraph partnerRel.contact["`**partnerRel.contact**`"]
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]]
+ role:partnerRel.contact:OWNER[[partnerRel.contact:OWNER]]
+ role:partnerRel.contact:ADMIN[[partnerRel.contact:ADMIN]]
+ role:partnerRel.contact:REFERRER[[partnerRel.contact:REFERRER]]
end
end
@@ -35,52 +35,14 @@ subgraph partner["`**partner**`"]
subgraph partnerRel["`**partnerRel**`"]
direction TB
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
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]]
+ role:partnerRel:OWNER[[partnerRel:OWNER]]
+ role:partnerRel:ADMIN[[partnerRel:ADMIN]]
+ role:partnerRel:AGENT[[partnerRel:AGENT]]
+ role:partnerRel:TENANT[[partnerRel:TENANT]]
end
end
end
@@ -105,9 +67,9 @@ subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
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]]
+ role:partnerRel.anchorPerson:OWNER[[partnerRel.anchorPerson:OWNER]]
+ role:partnerRel.anchorPerson:ADMIN[[partnerRel.anchorPerson:ADMIN]]
+ role:partnerRel.anchorPerson:REFERRER[[partnerRel.anchorPerson:REFERRER]]
end
end
@@ -118,41 +80,41 @@ subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
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]]
+ 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
+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
+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
```
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
index 9cdd92fc..b5510d8c 100644
--- a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
+++ b/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
@@ -42,12 +42,12 @@ begin
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 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; $$;
@@ -110,23 +110,23 @@ begin
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, '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, '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(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, '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, '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));
+ call revokePermissionFromRole(getPermissionId(oldPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAGENT(oldPartnerRel));
+ call grantPermissionToRole(createPermission(newPartnerDetails.uuid, 'SELECT'), hsOfficeRelationAGENT(newPartnerRel));
end if;
@@ -170,7 +170,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_partner'),
- globalAdmin());
+ globalADMIN());
END LOOP;
END;
$$;
@@ -185,7 +185,7 @@ create or replace function hs_office_partner_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner'),
- globalAdmin());
+ globalADMIN());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
index d27a1064..347896bb 100644
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
@@ -18,6 +18,6 @@ subgraph partnerDetails["`**partnerDetails**`"]
end
%% granting permissions to roles
-role:global:admin ==> perm:partnerDetails:INSERT
+role:global:ADMIN ==> perm:partnerDetails:INSERT
```
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
index a594823b..c99639bb 100644
--- a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
+++ b/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
@@ -74,7 +74,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_partner_details'),
- globalAdmin());
+ globalADMIN());
END LOOP;
END;
$$;
@@ -89,7 +89,7 @@ create or replace function hs_office_partner_details_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_partner_details'),
- globalAdmin());
+ globalADMIN());
return NEW;
end; $$;
@@ -107,8 +107,8 @@ create or replace function hs_office_partner_details_insert_permission_missing_t
returns trigger
language plpgsql as $$
begin
- raise exception '[403] insert into hs_office_partner_details not allowed for current subjects % (%) assumed by user % (%)',
- currentSubjects(), currentSubjectsUuids(), currentUser(), currentUserUuid();
+ 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
@@ -124,7 +124,7 @@ create trigger hs_office_partner_details_insert_permission_check_tg
call generateRbacIdentityViewFromQuery('hs_office_partner_details',
$idName$
- SELECT partnerDetails.uuid as uuid, partner_iv.idName || '-details' as 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
diff --git a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
index ae3ed66e..65017b18 100644
--- a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
+++ b/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
@@ -24,7 +24,7 @@ declare
begin
idName := cleanIdentifier( partnerPersonName|| '-' || contactLabel);
currentTask := 'creating partner test-data ' || idName;
- call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+ 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
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
index c33e3374..4558815c 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
@@ -13,9 +13,9 @@ subgraph bankAccount["`**bankAccount**`"]
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]]
+ role:bankAccount:OWNER[[bankAccount:OWNER]]
+ role:bankAccount:ADMIN[[bankAccount:ADMIN]]
+ role:bankAccount:REFERRER[[bankAccount:REFERRER]]
end
subgraph bankAccount:permissions[ ]
@@ -29,17 +29,17 @@ subgraph bankAccount["`**bankAccount**`"]
end
%% granting roles to users
-user:creator ==> role:bankAccount:owner
+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
+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
+role:global:GUEST ==> perm:bankAccount:INSERT
+role:bankAccount:OWNER ==> perm:bankAccount:DELETE
+role:bankAccount:ADMIN ==> perm:bankAccount:UPDATE
+role:bankAccount:REFERRER ==> perm:bankAccount:SELECT
```
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
index c4628183..c12c4c88 100644
--- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
+++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
@@ -35,22 +35,22 @@ begin
call enterTriggerForObjectUuid(NEW.uuid);
perform createRoleWithGrants(
- hsOfficeBankAccountOwner(NEW),
+ hsOfficeBankAccountOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
+ incomingSuperRoles => array[globalADMIN()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficeBankAccountAdmin(NEW),
+ hsOfficeBankAccountADMIN(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
+ incomingSuperRoles => array[hsOfficeBankAccountOWNER(NEW)]
);
perform createRoleWithGrants(
- hsOfficeBankAccountReferrer(NEW),
+ hsOfficeBankAccountREFERRER(NEW),
permissions => array['SELECT'],
- incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
+ incomingSuperRoles => array[hsOfficeBankAccountADMIN(NEW)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -93,7 +93,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount'),
- globalGuest());
+ globalGUEST());
END LOOP;
END;
$$;
@@ -108,7 +108,7 @@ create or replace function hs_office_bankaccount_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'),
- globalGuest());
+ globalGUEST());
return NEW;
end; $$;
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
index 43fb6ef3..aa3059f9 100644
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
@@ -13,9 +13,9 @@ subgraph bankAccount["`**bankAccount**`"]
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]]
+ role:bankAccount:OWNER[[bankAccount:OWNER]]
+ role:bankAccount:ADMIN[[bankAccount:ADMIN]]
+ role:bankAccount:REFERRER[[bankAccount:REFERRER]]
end
end
@@ -26,9 +26,9 @@ subgraph debitorRel.contact["`**debitorRel.contact**`"]
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]]
+ role:debitorRel.contact:OWNER[[debitorRel.contact:OWNER]]
+ role:debitorRel.contact:ADMIN[[debitorRel.contact:ADMIN]]
+ role:debitorRel.contact:REFERRER[[debitorRel.contact:REFERRER]]
end
end
@@ -39,9 +39,9 @@ subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
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]]
+ role:debitorRel.anchorPerson:OWNER[[debitorRel.anchorPerson:OWNER]]
+ role:debitorRel.anchorPerson:ADMIN[[debitorRel.anchorPerson:ADMIN]]
+ role:debitorRel.anchorPerson:REFERRER[[debitorRel.anchorPerson:REFERRER]]
end
end
@@ -52,9 +52,9 @@ subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
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]]
+ role:debitorRel.holderPerson:OWNER[[debitorRel.holderPerson:OWNER]]
+ role:debitorRel.holderPerson:ADMIN[[debitorRel.holderPerson:ADMIN]]
+ role:debitorRel.holderPerson:REFERRER[[debitorRel.holderPerson:REFERRER]]
end
end
@@ -65,10 +65,10 @@ subgraph sepaMandate["`**sepaMandate**`"]
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]]
+ role:sepaMandate:OWNER[[sepaMandate:OWNER]]
+ role:sepaMandate:ADMIN[[sepaMandate:ADMIN]]
+ role:sepaMandate:AGENT[[sepaMandate:AGENT]]
+ role:sepaMandate:REFERRER[[sepaMandate:REFERRER]]
end
subgraph sepaMandate:permissions[ ]
@@ -85,96 +85,57 @@ subgraph debitorRel["`**debitorRel**`"]
direction TB
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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]]
+ 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
+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
+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
+role:sepaMandate:OWNER ==> perm:sepaMandate:DELETE
+role:sepaMandate:ADMIN ==> perm:sepaMandate:UPDATE
+role:sepaMandate:REFERRER ==> perm:sepaMandate:SELECT
+role:debitorRel:ADMIN ==> perm:sepaMandate:INSERT
```
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
index 0f168fd5..9f126a22 100644
--- a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
+++ b/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
@@ -48,34 +48,34 @@ begin
perform createRoleWithGrants(
- hsOfficeSepaMandateOwner(NEW),
+ hsOfficeSepaMandateOWNER(NEW),
permissions => array['DELETE'],
- incomingSuperRoles => array[globalAdmin()],
+ incomingSuperRoles => array[globalADMIN()],
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficeSepaMandateAdmin(NEW),
+ hsOfficeSepaMandateADMIN(NEW),
permissions => array['UPDATE'],
- incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
+ incomingSuperRoles => array[hsOfficeSepaMandateOWNER(NEW)]
);
perform createRoleWithGrants(
- hsOfficeSepaMandateAgent(NEW),
- incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
+ hsOfficeSepaMandateAGENT(NEW),
+ incomingSuperRoles => array[hsOfficeSepaMandateADMIN(NEW)],
outgoingSubRoles => array[
- hsOfficeBankAccountReferrer(newBankAccount),
- hsOfficeRelationAgent(newDebitorRel)]
+ hsOfficeBankAccountREFERRER(newBankAccount),
+ hsOfficeRelationAGENT(newDebitorRel)]
);
perform createRoleWithGrants(
- hsOfficeSepaMandateReferrer(NEW),
+ hsOfficeSepaMandateREFERRER(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
- hsOfficeBankAccountAdmin(newBankAccount),
- hsOfficeRelationAgent(newDebitorRel),
- hsOfficeSepaMandateAgent(NEW)],
- outgoingSubRoles => array[hsOfficeRelationTenant(newDebitorRel)]
+ hsOfficeBankAccountADMIN(newBankAccount),
+ hsOfficeRelationAGENT(newDebitorRel),
+ hsOfficeSepaMandateAGENT(NEW)],
+ outgoingSubRoles => array[hsOfficeRelationTENANT(newDebitorRel)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -118,7 +118,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_sepamandate'),
- hsOfficeRelationAdmin(row));
+ hsOfficeRelationADMIN(row));
END LOOP;
END;
$$;
@@ -133,7 +133,7 @@ create or replace function hs_office_sepamandate_hs_office_relation_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_sepamandate'),
- hsOfficeRelationAdmin(NEW));
+ hsOfficeRelationADMIN(NEW));
return NEW;
end; $$;
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
index 11999980..69d39165 100644
--- 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
@@ -20,7 +20,7 @@ declare
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');
+ call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
execute format('set local hsadminng.currentTask to %L', currentTask);
select debitor.* into relatedDebitor
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 a1baa702..5c43e03d 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
@@ -13,9 +13,9 @@ subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
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]]
+ role:debitorRel.anchorPerson:OWNER[[debitorRel.anchorPerson:OWNER]]
+ role:debitorRel.anchorPerson:ADMIN[[debitorRel.anchorPerson:ADMIN]]
+ role:debitorRel.anchorPerson:REFERRER[[debitorRel.anchorPerson:REFERRER]]
end
end
@@ -26,9 +26,9 @@ subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
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]]
+ role:debitorRel.holderPerson:OWNER[[debitorRel.holderPerson:OWNER]]
+ role:debitorRel.holderPerson:ADMIN[[debitorRel.holderPerson:ADMIN]]
+ role:debitorRel.holderPerson:REFERRER[[debitorRel.holderPerson:REFERRER]]
end
end
@@ -39,9 +39,9 @@ subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
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]]
+ role:partnerRel.holderPerson:OWNER[[partnerRel.holderPerson:OWNER]]
+ role:partnerRel.holderPerson:ADMIN[[partnerRel.holderPerson:ADMIN]]
+ role:partnerRel.holderPerson:REFERRER[[partnerRel.holderPerson:REFERRER]]
end
end
@@ -61,52 +61,14 @@ subgraph debitor["`**debitor**`"]
subgraph debitorRel["`**debitorRel**`"]
direction TB
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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: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]]
+ role:debitorRel:OWNER[[debitorRel:OWNER]]
+ role:debitorRel:ADMIN[[debitorRel:ADMIN]]
+ role:debitorRel:AGENT[[debitorRel:AGENT]]
+ role:debitorRel:TENANT[[debitorRel:TENANT]]
end
end
end
@@ -115,52 +77,13 @@ subgraph partnerRel["`**partnerRel**`"]
direction TB
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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 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: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]]
+ role:partnerRel:OWNER[[partnerRel:OWNER]]
+ role:partnerRel:ADMIN[[partnerRel:ADMIN]]
+ role:partnerRel:AGENT[[partnerRel:AGENT]]
+ role:partnerRel:TENANT[[partnerRel:TENANT]]
end
end
@@ -171,9 +94,9 @@ subgraph partnerRel.contact["`**partnerRel.contact**`"]
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]]
+ role:partnerRel.contact:OWNER[[partnerRel.contact:OWNER]]
+ role:partnerRel.contact:ADMIN[[partnerRel.contact:ADMIN]]
+ role:partnerRel.contact:REFERRER[[partnerRel.contact:REFERRER]]
end
end
@@ -184,9 +107,9 @@ subgraph debitorRel.contact["`**debitorRel.contact**`"]
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]]
+ role:debitorRel.contact:OWNER[[debitorRel.contact:OWNER]]
+ role:debitorRel.contact:ADMIN[[debitorRel.contact:ADMIN]]
+ role:debitorRel.contact:REFERRER[[debitorRel.contact:REFERRER]]
end
end
@@ -197,9 +120,9 @@ subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
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]]
+ role:partnerRel.anchorPerson:OWNER[[partnerRel.anchorPerson:OWNER]]
+ role:partnerRel.anchorPerson:ADMIN[[partnerRel.anchorPerson:ADMIN]]
+ role:partnerRel.anchorPerson:REFERRER[[partnerRel.anchorPerson:REFERRER]]
end
end
@@ -210,66 +133,66 @@ subgraph refundBankAccount["`**refundBankAccount**`"]
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]]
+ 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
+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
+role:global:ADMIN ==> perm:debitor:INSERT
+role:debitorRel:OWNER ==> perm:debitor:DELETE
+role:debitorRel:ADMIN ==> perm:debitor:UPDATE
+role:debitorRel:TENANT ==> perm:debitor:SELECT
```
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
index 065efff6..152f980e 100644
--- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
+++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
@@ -51,15 +51,15 @@ begin
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 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 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; $$;
@@ -143,7 +143,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_debitor'),
- globalAdmin());
+ globalADMIN());
END LOOP;
END;
$$;
@@ -158,7 +158,7 @@ create or replace function hs_office_debitor_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'),
- globalAdmin());
+ globalADMIN());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
index 5a485b31..ed965104 100644
--- a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
+++ b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
@@ -23,7 +23,7 @@ declare
begin
idName := cleanIdentifier( forPartnerPersonName|| '-' || forBillingContactLabel);
currentTask := 'creating debitor test-data ' || idName;
- call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+ call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
execute format('set local hsadminng.currentTask to %L', currentTask);
select debitorRel.uuid
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
index 339f9eb0..3681b8e6 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.md
@@ -10,52 +10,13 @@ subgraph partnerRel["`**partnerRel**`"]
direction TB
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
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]]
+ role:partnerRel:OWNER[[partnerRel:OWNER]]
+ role:partnerRel:ADMIN[[partnerRel:ADMIN]]
+ role:partnerRel:AGENT[[partnerRel:AGENT]]
+ role:partnerRel:TENANT[[partnerRel:TENANT]]
end
end
@@ -66,9 +27,9 @@ subgraph partnerRel.contact["`**partnerRel.contact**`"]
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]]
+ role:partnerRel.contact:OWNER[[partnerRel.contact:OWNER]]
+ role:partnerRel.contact:ADMIN[[partnerRel.contact:ADMIN]]
+ role:partnerRel.contact:REFERRER[[partnerRel.contact:REFERRER]]
end
end
@@ -79,9 +40,9 @@ subgraph membership["`**membership**`"]
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]]
+ role:membership:OWNER[[membership:OWNER]]
+ role:membership:ADMIN[[membership:ADMIN]]
+ role:membership:AGENT[[membership:AGENT]]
end
subgraph membership:permissions[ ]
@@ -101,9 +62,9 @@ subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
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]]
+ role:partnerRel.anchorPerson:OWNER[[partnerRel.anchorPerson:OWNER]]
+ role:partnerRel.anchorPerson:ADMIN[[partnerRel.anchorPerson:ADMIN]]
+ role:partnerRel.anchorPerson:REFERRER[[partnerRel.anchorPerson:REFERRER]]
end
end
@@ -114,46 +75,46 @@ subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
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]]
+ 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
+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
+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
+role:global:ADMIN ==> perm:membership:INSERT
+role:membership:ADMIN ==> perm:membership:DELETE
+role:membership:ADMIN ==> perm:membership:UPDATE
+role:membership:AGENT ==> perm:membership:SELECT
```
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
index 4f34cee8..7f8de66b 100644
--- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
+++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
@@ -44,25 +44,25 @@ begin
perform createRoleWithGrants(
- hsOfficeMembershipOwner(NEW),
+ hsOfficeMembershipOWNER(NEW),
userUuids => array[currentUserUuid()]
);
perform createRoleWithGrants(
- hsOfficeMembershipAdmin(NEW),
+ hsOfficeMembershipADMIN(NEW),
permissions => array['DELETE', 'UPDATE'],
incomingSuperRoles => array[
- hsOfficeMembershipOwner(NEW),
- hsOfficeRelationAdmin(newPartnerRel)]
+ hsOfficeMembershipOWNER(NEW),
+ hsOfficeRelationADMIN(newPartnerRel)]
);
perform createRoleWithGrants(
- hsOfficeMembershipAgent(NEW),
+ hsOfficeMembershipAGENT(NEW),
permissions => array['SELECT'],
incomingSuperRoles => array[
- hsOfficeMembershipAdmin(NEW),
- hsOfficeRelationAgent(newPartnerRel)],
- outgoingSubRoles => array[hsOfficeRelationTenant(newPartnerRel)]
+ hsOfficeMembershipADMIN(NEW),
+ hsOfficeRelationAGENT(newPartnerRel)],
+ outgoingSubRoles => array[hsOfficeRelationTENANT(newPartnerRel)]
);
call leaveTriggerForObjectUuid(NEW.uuid);
@@ -105,7 +105,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_membership'),
- globalAdmin());
+ globalADMIN());
END LOOP;
END;
$$;
@@ -120,7 +120,7 @@ create or replace function hs_office_membership_global_insert_tf()
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_membership'),
- globalAdmin());
+ globalADMIN());
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
index 9d574a58..d49a5344 100644
--- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
+++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
@@ -19,7 +19,7 @@ begin
currentTask := 'creating Membership test-data ' ||
'P-' || forPartnerNumber::text ||
'M-...' || newMemberNumberSuffix;
- call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
+ 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
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
index 70f268a8..26ff3d5c 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
@@ -13,9 +13,9 @@ subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPers
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]]
+ 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
@@ -26,9 +26,9 @@ subgraph membership.partnerRel.anchorPerson["`**membership.partnerRel.anchorPers
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]]
+ 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
@@ -49,103 +49,12 @@ subgraph membership["`**membership**`"]
direction TB
style membership fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 membership.partnerRel["`**membership.partnerRel**`"]
- direction TB
- style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
- 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
-
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]]
+ role:membership:OWNER[[membership:OWNER]]
+ role:membership:ADMIN[[membership:ADMIN]]
+ role:membership:AGENT[[membership:AGENT]]
end
end
@@ -153,52 +62,13 @@ subgraph membership.partnerRel["`**membership.partnerRel**`"]
direction TB
style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
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]]
+ 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
@@ -209,42 +79,42 @@ subgraph membership.partnerRel.contact["`**membership.partnerRel.contact**`"]
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]]
+ 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
+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
+role:membership:ADMIN ==> perm:coopSharesTransaction:INSERT
+role:membership:ADMIN ==> perm:coopSharesTransaction:UPDATE
+role:membership:AGENT ==> perm:coopSharesTransaction:SELECT
```
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
index 2cdfa55c..f4856f0a 100644
--- a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
+++ b/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
@@ -38,8 +38,8 @@ begin
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 grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
+ call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
@@ -81,7 +81,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_coopsharestransaction'),
- hsOfficeMembershipAdmin(row));
+ hsOfficeMembershipADMIN(row));
END LOOP;
END;
$$;
@@ -96,7 +96,7 @@ create or replace function hs_office_coopsharestransaction_hs_office_membership_
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_coopsharestransaction'),
- hsOfficeMembershipAdmin(NEW));
+ hsOfficeMembershipADMIN(NEW));
return NEW;
end; $$;
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
index 210bd69f..d220a38c 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
@@ -13,9 +13,9 @@ subgraph membership.partnerRel.holderPerson["`**membership.partnerRel.holderPers
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]]
+ 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
@@ -26,9 +26,9 @@ subgraph membership.partnerRel.anchorPerson["`**membership.partnerRel.anchorPers
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]]
+ 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
@@ -49,103 +49,12 @@ subgraph membership["`**membership**`"]
direction TB
style membership fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 membership.partnerRel["`**membership.partnerRel**`"]
- direction TB
- style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
- 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
-
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]]
+ role:membership:OWNER[[membership:OWNER]]
+ role:membership:ADMIN[[membership:ADMIN]]
+ role:membership:AGENT[[membership:AGENT]]
end
end
@@ -153,52 +62,13 @@ subgraph membership.partnerRel["`**membership.partnerRel**`"]
direction TB
style membership.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
- 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 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
-
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]]
+ 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
@@ -209,42 +79,42 @@ subgraph membership.partnerRel.contact["`**membership.partnerRel.contact**`"]
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]]
+ 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
+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
+role:membership:ADMIN ==> perm:coopAssetsTransaction:INSERT
+role:membership:ADMIN ==> perm:coopAssetsTransaction:UPDATE
+role:membership:AGENT ==> perm:coopAssetsTransaction:SELECT
```
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
index 4dda4e2e..df1fdd3b 100644
--- a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
+++ b/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
@@ -38,8 +38,8 @@ begin
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 grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeMembershipAGENT(newMembership));
+ call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeMembershipADMIN(newMembership));
call leaveTriggerForObjectUuid(NEW.uuid);
end; $$;
@@ -81,7 +81,7 @@ do language plpgsql $$
LOOP
call grantPermissionToRole(
createPermission(row.uuid, 'INSERT', 'hs_office_coopassetstransaction'),
- hsOfficeMembershipAdmin(row));
+ hsOfficeMembershipADMIN(row));
END LOOP;
END;
$$;
@@ -96,7 +96,7 @@ create or replace function hs_office_coopassetstransaction_hs_office_membership_
begin
call grantPermissionToRole(
createPermission(NEW.uuid, 'INSERT', 'hs_office_coopassetstransaction'),
- hsOfficeMembershipAdmin(NEW));
+ hsOfficeMembershipADMIN(NEW));
return NEW;
end; $$;
diff --git a/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java
index c02cb944..0daa0a15 100644
--- a/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java
+++ b/src/test/java/net/hostsharing/hsadminng/context/ContextIntegrationTests.java
@@ -59,13 +59,13 @@ class ContextIntegrationTests {
void defineWithoutCurrentUserButWithAssumedRoles() {
// when
final var result = jpaAttempt.transacted(() ->
- context.define(null, "test_package#yyy00.admin")
+ context.define(null, "test_package#yyy00:ADMIN")
);
// then
result.assertExceptionWithRootCauseMessage(
jakarta.persistence.PersistenceException.class,
- "ERROR: [403] undefined has no permission to assume role test_package#yyy00.admin");
+ "ERROR: [403] undefined has no permission to assume role test_package#yyy00:ADMIN");
}
@Test
@@ -85,7 +85,7 @@ class ContextIntegrationTests {
@Transactional
void defineWithCurrentUserAndAssumedRoles() {
// given
- context.define("superuser-alex@hostsharing.net", "test_customer#xxx.owner;test_customer#yyy.owner");
+ context.define("superuser-alex@hostsharing.net", "test_customer#xxx:OWNER;test_customer#yyy:OWNER");
// when
final var currentUser = context.getCurrentUser();
@@ -93,7 +93,7 @@ class ContextIntegrationTests {
// then
assertThat(context.getAssumedRoles())
- .isEqualTo(Array.of("test_customer#xxx.owner", "test_customer#yyy.owner"));
+ .isEqualTo(Array.of("test_customer#xxx:OWNER", "test_customer#yyy:OWNER"));
assertThat(context.currentSubjectsUuids()).hasSize(2);
}
@@ -101,12 +101,12 @@ class ContextIntegrationTests {
public void defineContextWithCurrentUserAndAssumeInaccessibleRole() {
// when
final var result = jpaAttempt.transacted(() ->
- context.define("customer-admin@xxx.example.com", "test_package#yyy00.admin")
+ context.define("customer-admin@xxx.example.com", "test_package#yyy00:ADMIN")
);
// then
result.assertExceptionWithRootCauseMessage(
jakarta.persistence.PersistenceException.class,
- "ERROR: [403] user customer-admin@xxx.example.com has no permission to assume role test_package#yyy00.admin");
+ "ERROR: [403] user customer-admin@xxx.example.com has no permission to assume role test_package#yyy00:ADMIN");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
index fd484c4c..f0541813 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java
@@ -102,21 +102,21 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
final var roles = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(roles)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_bankaccount#DE25500105176934832579.owner",
- "hs_office_bankaccount#DE25500105176934832579.admin",
- "hs_office_bankaccount#DE25500105176934832579.referrer"
+ "hs_office_bankaccount#DE25500105176934832579:OWNER",
+ "hs_office_bankaccount#DE25500105176934832579:ADMIN",
+ "hs_office_bankaccount#DE25500105176934832579:REFERRER"
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm DELETE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
- "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to role global#global.admin by system and assume }",
- "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to user selfregistered-user-drew@hostsharing.org by hs_office_bankaccount#DE25500105176934832579.owner and assume }",
+ "{ grant perm:hs_office_bankaccount#DE25500105176934832579:DELETE to role:hs_office_bankaccount#DE25500105176934832579:OWNER by system and assume }",
+ "{ grant role:hs_office_bankaccount#DE25500105176934832579:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant role:hs_office_bankaccount#DE25500105176934832579:OWNER to user:selfregistered-user-drew@hostsharing.org by hs_office_bankaccount#DE25500105176934832579:OWNER and assume }",
- "{ grant role hs_office_bankaccount#DE25500105176934832579.admin to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }",
- "{ grant perm UPDATE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
+ "{ grant role:hs_office_bankaccount#DE25500105176934832579:ADMIN to role:hs_office_bankaccount#DE25500105176934832579:OWNER by system and assume }",
+ "{ grant perm:hs_office_bankaccount#DE25500105176934832579:UPDATE to role:hs_office_bankaccount#DE25500105176934832579:ADMIN by system and assume }",
- "{ grant perm SELECT on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.referrer by system and assume }",
- "{ grant role hs_office_bankaccount#DE25500105176934832579.referrer to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }",
+ "{ grant perm:hs_office_bankaccount#DE25500105176934832579:SELECT to role:hs_office_bankaccount#DE25500105176934832579:REFERRER by system and assume }",
+ "{ grant role:hs_office_bankaccount#DE25500105176934832579:REFERRER to role:hs_office_bankaccount#DE25500105176934832579:ADMIN by system and assume }",
null
));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
index 259f88fe..3187a4f4 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java
@@ -103,20 +103,20 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean
final var roles = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(roles)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_contact#anothernewcontact.owner",
- "hs_office_contact#anothernewcontact.admin",
- "hs_office_contact#anothernewcontact.referrer"
+ "hs_office_contact#anothernewcontact:OWNER",
+ "hs_office_contact#anothernewcontact:ADMIN",
+ "hs_office_contact#anothernewcontact:REFERRER"
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }",
- "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by hs_office_contact#anothernewcontact.owner and assume }",
- "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
+ "{ grant role:hs_office_contact#anothernewcontact:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant perm:hs_office_contact#anothernewcontact:UPDATE to role:hs_office_contact#anothernewcontact:ADMIN by system and assume }",
+ "{ grant role:hs_office_contact#anothernewcontact:OWNER to user:selfregistered-user-drew@hostsharing.org by hs_office_contact#anothernewcontact:OWNER and assume }",
+ "{ grant perm:hs_office_contact#anothernewcontact:DELETE to role:hs_office_contact#anothernewcontact:OWNER by system and assume }",
+ "{ grant role:hs_office_contact#anothernewcontact:ADMIN to role:hs_office_contact#anothernewcontact:OWNER by system and assume }",
- "{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.referrer by system and assume }",
- "{ grant role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }"
+ "{ grant perm:hs_office_contact#anothernewcontact:SELECT to role:hs_office_contact#anothernewcontact:REFERRER by system and assume }",
+ "{ grant role:hs_office_contact#anothernewcontact:REFERRER to role:hs_office_contact#anothernewcontact:ADMIN by system and assume }"
));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
index d6607501..978e2081 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java
@@ -112,8 +112,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopassetstransaction#temprefB to role membership#M-1000101.agent by system and assume }",
- "{ grant perm UPDATE on coopassetstransaction#temprefB to role membership#M-1000101.admin by system and assume }",
+ "{ grant perm:coopassetstransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
+ "{ grant perm:coopassetstransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null));
}
@@ -194,7 +194,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void partnerPersonAdmin_canViewRelatedCoopAssetsTransactions() {
// given:
- context("superuser-alex@hostsharing.net", "hs_office_person#FirstGmbH.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_person#FirstGmbH:ADMIN");
// when:
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
index ed649f15..eff83079 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java
@@ -111,8 +111,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm SELECT on coopsharestransaction#temprefB to role membership#M-1000101.agent by system and assume }",
- "{ grant perm UPDATE on coopsharestransaction#temprefB to role membership#M-1000101.admin by system and assume }",
+ "{ grant perm:coopsharestransaction#temprefB:SELECT to role:membership#M-1000101:AGENT by system and assume }",
+ "{ grant perm:coopsharestransaction#temprefB:UPDATE to role:membership#M-1000101:ADMIN by system and assume }",
null));
}
@@ -193,7 +193,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given:
- context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000101.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000101:ADMIN");
// when:
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
index 975ad961..c2e3fffd 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
@@ -635,7 +635,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_contact#fourthcontact.admin")
+ .header("assumed-roles", "hs_office_contact#fourthcontact:ADMIN")
.contentType(ContentType.JSON)
.body("""
{
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
index 5f53df24..7a3dfbb7 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
@@ -172,44 +172,44 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.owner",
- "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.admin",
- "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.agent",
- "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG.tenant"));
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT",
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
- "{ grant perm INSERT into sepamandate with relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
+ "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:INSERT>sepamandate to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
// owner
- "{ grant perm DELETE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
- "{ grant perm DELETE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to role global#global.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.owner to user superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG.owner and assume }",
+ "{ grant perm:debitor#D-1000122:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
+ "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:DELETE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER to user:superuser-alex@hostsharing.net by relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER and assume }",
// admin
- "{ grant perm UPDATE on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
- "{ grant perm UPDATE on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#FirstGmbH-with-DEBITOR-FourtheG.owner by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role person#FirstGmbH.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
+ "{ grant perm:debitor#D-1000122:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
+ "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:UPDATE to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#FirstGmbH-with-DEBITOR-FourtheG:OWNER by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:person#FirstGmbH:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
// agent
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role person#FourtheG.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#FirstGmbH-with-DEBITOR-FourtheG.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.agent to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT to role:person#FourtheG:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
// tenant
- "{ grant perm SELECT on debitor#D-1000122 to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
- "{ grant perm SELECT on relation#FirstGmbH-with-DEBITOR-FourtheG to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
- "{ grant role contact#fourthcontact.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
- "{ grant role person#FirstGmbH.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
- "{ grant role person#FourtheG.referrer to role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role contact#fourthcontact.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role person#FourtheG.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FourtheG.tenant to role relation#FirstGmbH-with-DEBITOR-FourtheG.agent by system and assume }",
+ "{ grant perm:debitor#D-1000122:SELECT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
+ "{ grant perm:relation#FirstGmbH-with-DEBITOR-FourtheG:SELECT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT by system and assume }",
+ "{ grant role:contact#fourthcontact:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
+ "{ grant role:person#FirstGmbH:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
+ "{ grant role:person#FourtheG:REFERRER to role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:contact#fourthcontact:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:person#FourtheG:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FourtheG:TENANT to role:relation#FirstGmbH-with-DEBITOR-FourtheG:AGENT by system and assume }",
null));
}
@@ -243,9 +243,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@ParameterizedTest
@Disabled // TODO: reactivate once partner.person + partner.contact are removed
@ValueSource(strings = {
- "hs_office_partner#10001:FirstGmbH-firstcontact.admin",
- "hs_office_person#FirstGmbH.admin",
- "hs_office_contact#firstcontact.admin",
+ "hs_office_partner#10001:FirstGmbH-firstcontact:ADMIN",
+ "hs_office_person#FirstGmbH:ADMIN",
+ "hs_office_contact#firstcontact:ADMIN",
})
public void relatedPersonAdmin_canViewRelatedDebitors(final String assumedRole) {
// given:
@@ -317,7 +317,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN", true);
final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
final var givenNewBillingPerson = one(personRepo.findPersonByOptionalNameLike("Firby"));
final var givenNewContact = one(contactRepo.findContactByOptionalLabelLike("sixth contact"));
@@ -346,31 +346,31 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin", true);
+ "global#global:ADMIN", true);
// ... partner role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan.agent", true);
+ "hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan:AGENT", true);
// ... contact role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_contact#fifthcontact.admin");
+ "hs_office_contact#fifthcontact:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_contact#sixthcontact.admin", false);
+ "hs_office_contact#sixthcontact:ADMIN", false);
// ... bank-account role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#DE02200505501015871393.admin");
+ "hs_office_bankaccount#DE02200505501015871393:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#DE02120300000000202051.admin", true);
+ "hs_office_bankaccount#DE02120300000000202051:ADMIN", true);
}
@Test
@@ -380,7 +380,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin", true);
+ "hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
@@ -395,12 +395,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin", true);
+ "global#global:ADMIN", true);
// ... bank-account role was assigned:
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#DE02120300000000202051.admin", true);
+ "hs_office_bankaccount#DE02120300000000202051:ADMIN", true);
}
@Test
@@ -410,7 +410,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fih");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
+ "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
// when
@@ -424,12 +424,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin", true);
+ "global#global:ADMIN", true);
// ... bank-account role was removed from previous bank-account admin:
assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_bankaccount#DE02200505501015871393.admin");
+ "hs_office_bankaccount#DE02200505501015871393:ADMIN");
}
@Test
@@ -439,12 +439,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent", true);
+ "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG.agent");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT");
givenDebitor.setVatId("NEW-VAT-ID");
return toCleanup(debitorRepo.save(givenDebitor));
});
@@ -462,11 +462,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThatDebitorActuallyInDatabase(givenDebitor, true);
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
- "hs_office_contact#ninthcontact.admin", false);
+ "hs_office_contact#ninthcontact:ADMIN", false);
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact:ADMIN");
givenDebitor.setVatId("NEW-VAT-ID");
return toCleanup(debitorRepo.save(givenDebitor));
});
@@ -545,7 +545,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_relation#FourtheG-with-DEBITOR-FourtheG.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent();
debitorRepo.deleteByUuid(givenDebitor.getUuid());
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
index 51ad5b4c..f3601449 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
@@ -269,7 +269,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-ThirdOHG.agent")
+ .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-ThirdOHG:AGENT")
.port(port)
.when()
.get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid)
@@ -338,15 +338,15 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
void partnerRelAdmin_canPatchValidityOfRelatedMembership() {
// given
- final var givenPartnerAgent = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.admin";
- context.define("superuser-alex@hostsharing.net", givenPartnerAgent);
+ final var givenPartnerAdmin = "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN";
+ context.define("superuser-alex@hostsharing.net", givenPartnerAdmin);
final var givenMembership = givenSomeTemporaryMembershipBessler("First");
// when
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", givenPartnerAgent)
+ .header("assumed-roles", givenPartnerAdmin)
.contentType(ContentType.JSON)
.body("""
{
@@ -401,7 +401,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent")
+ .header("assumed-roles", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT")
.port(port)
.when()
.delete("http://localhost/api/hs/office/memberships/" + givenMembership.getUuid())
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
index fcf2e976..1659c929 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
@@ -91,7 +91,6 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
context("superuser-alex@hostsharing.net");
final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()).stream()
- .map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
.toList();
@@ -111,33 +110,32 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_membership#M-1000117.admin",
- "hs_office_membership#M-1000117.owner",
- "hs_office_membership#M-1000117.agent"));
+ "hs_office_membership#M-1000117:OWNER",
+ "hs_office_membership#M-1000117:ADMIN",
+ "hs_office_membership#M-1000117:AGENT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
- .map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
-
// insert
- "{ grant perm INSERT into coopassetstransaction with membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
- "{ grant perm INSERT into coopsharestransaction with membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
+ "{ grant perm:membership#M-1000117:INSERT>coopassetstransaction to role:membership#M-1000117:ADMIN by system and assume }",
+ "{ grant perm:membership#M-1000117:INSERT>coopsharestransaction to role:membership#M-1000117:ADMIN by system and assume }",
// owner
- "{ grant perm DELETE on membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
- "{ grant role membership#M-1000117.owner to user superuser-alex@hostsharing.net by membership#M-1000117.owner and assume }",
+ "{ grant perm:membership#M-1000117:DELETE to role:membership#M-1000117:ADMIN by system and assume }",
+ "{ grant role:membership#M-1000117:OWNER to user:superuser-alex@hostsharing.net by membership#M-1000117:OWNER and assume }",
// admin
- "{ grant perm UPDATE on membership#M-1000117 to role membership#M-1000117.admin by system and assume }",
- "{ grant role membership#M-1000117.admin to role membership#M-1000117.owner by system and assume }",
- "{ grant role membership#M-1000117.admin to role relation#HostsharingeG-with-PARTNER-FirstGmbH.admin by system and assume }",
+ "{ grant perm:membership#M-1000117:UPDATE to role:membership#M-1000117:ADMIN by system and assume }",
+ "{ grant role:membership#M-1000117:ADMIN to role:membership#M-1000117:OWNER by system and assume }",
+ "{ grant role:membership#M-1000117:ADMIN to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:ADMIN by system and assume }",
// agent
- "{ grant perm SELECT on membership#M-1000117 to role membership#M-1000117.agent by system and assume }",
- "{ grant role membership#M-1000117.agent to role membership#M-1000117.admin by system and assume }",
- "{ grant role membership#M-1000117.agent to role relation#HostsharingeG-with-PARTNER-FirstGmbH.agent by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-FirstGmbH.tenant to role membership#M-1000117.agent by system and assume }",
+ "{ grant perm:membership#M-1000117:SELECT to role:membership#M-1000117:AGENT by system and assume }",
+ "{ grant role:membership#M-1000117:AGENT to role:membership#M-1000117:ADMIN by system and assume }",
+
+ "{ grant role:membership#M-1000117:AGENT to role:relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-FirstGmbH:TENANT to role:membership#M-1000117:AGENT by system and assume }",
null));
}
@@ -232,13 +230,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
assertThatMembershipIsVisibleForRole(
givenMembership,
- "hs_office_membership#M-1000113.agent");
+ "hs_office_membership#M-1000113:AGENT");
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
// TODO: we should test with debitor- and partner-admin as well
- context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113.agent");
+ context("superuser-alex@hostsharing.net", "hs_office_membership#M-1000113:AGENT");
givenMembership.setValidity(
Range.closedOpen(givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);
@@ -296,7 +294,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH.agent");
+ context("superuser-alex@hostsharing.net", "hs_office_relation#HostsharingeG-with-PARTNER-FirstGmbH:AGENT");
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent();
membershipRepo.deleteByUuid(givenMembership.getUuid());
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index bb42901d..4010167d 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -106,7 +106,7 @@ import static org.assertj.core.api.Fail.fail;
@Tag("import")
@DataJpaTest(properties = {
"spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}",
- "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:admin}",
+ "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}",
"spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}",
"hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}"
})
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
index 94bcb9fe..98bff812 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java
@@ -132,52 +132,52 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.owner",
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.admin",
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.agent",
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant"));
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:OWNER",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:ADMIN",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:AGENT",
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("fourthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(distinct(fromFormatted(
initialGrantNames,
- "{ grant perm INSERT into sepamandate with relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:INSERT>sepamandate to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
// permissions on partner
- "{ grant perm DELETE on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm UPDATE on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
- "{ grant perm SELECT on partner#P-20032 to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant perm:partner#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
+ "{ grant perm:partner#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
+ "{ grant perm:partner#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
// permissions on partner-details
- "{ grant perm DELETE on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm UPDATE on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
- "{ grant perm SELECT on partner_details#P-20032-details to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
+ "{ grant perm:partner_details#P-20032:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
+ "{ grant perm:partner_details#P-20032:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
+ "{ grant perm:partner_details#P-20032:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
// permissions on partner-relation
- "{ grant perm DELETE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant perm UPDATE on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
- "{ grant perm SELECT on relation#HostsharingeG-with-PARTNER-EBess to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
+ "{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:DELETE to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",
+ "{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:UPDATE to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
+ "{ grant perm:relation#HostsharingeG-with-PARTNER-EBess:SELECT to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
// relation owner
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.owner to user superuser-alex@hostsharing.net by relation#HostsharingeG-with-PARTNER-EBess.owner and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:OWNER to user:superuser-alex@hostsharing.net by relation#HostsharingeG-with-PARTNER-EBess:OWNER and assume }",
// relation admin
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role relation#HostsharingeG-with-PARTNER-EBess.owner by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.admin to role person#HostsharingeG.admin by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN to role:relation#HostsharingeG-with-PARTNER-EBess:OWNER by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN to role:person#HostsharingeG:ADMIN by system and assume }",
// relation agent
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.agent to role person#EBess.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.agent to role relation#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:AGENT to role:person#EBess:ADMIN by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:AGENT to role:relation#HostsharingeG-with-PARTNER-EBess:ADMIN by system and assume }",
// relation tenant
- "{ grant role contact#4th.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#EBess.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role person#HostsharingeG.referrer to role relation#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
- "{ grant role relation#HostsharingeG-with-PARTNER-EBess.tenant to role relation#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
+ "{ grant role:contact#4th:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
+ "{ grant role:person#EBess:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
+ "{ grant role:person#HostsharingeG:REFERRER to role:relation#HostsharingeG-with-PARTNER-EBess:TENANT by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:contact#4th:ADMIN by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:person#EBess:ADMIN by system and assume }",
+ "{ grant role:relation#HostsharingeG-with-PARTNER-EBess:TENANT to role:relation#HostsharingeG-with-PARTNER-EBess:AGENT by system and assume }",
null)));
}
@@ -266,7 +266,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartner = givenSomeTemporaryHostsharingPartner(20036, "Erben Bessler", "fifth contact");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatPartnerActuallyInDatabase(givenPartner);
// when
@@ -281,13 +281,13 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "global#global.admin");
+ "global#global:ADMIN");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_person#ThirdOHG.admin");
+ "hs_office_person#ThirdOHG:ADMIN");
assertThatPartnerIsNotVisibleForUserWithRole(
givenPartner,
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
}
@Test
@@ -297,13 +297,13 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatPartnerActuallyInDatabase(givenPartner);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net",
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner);
});
@@ -319,20 +319,20 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenPartner = givenSomeTemporaryHostsharingPartner(20037, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatPartnerActuallyInDatabase(givenPartner);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net",
- "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant");
+ "hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:TENANT");
givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner);
});
// then
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
- "[403] insert into hs_office_partner_details not allowed for current subjects {hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler.tenant}");
+ "[403] insert into hs_office_partner_details not allowed for current subjects {hs_office_relation#HostsharingeG-with-PARTNER-ErbenBesslerMelBessler:TENANT}");
}
private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) {
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
index de198b47..ca4d82d4 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java
@@ -102,23 +102,23 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(
Array.from(
initialRoleNames,
- "hs_office_person#anothernewperson.owner",
- "hs_office_person#anothernewperson.admin",
- "hs_office_person#anothernewperson.referrer"
+ "hs_office_person#anothernewperson:OWNER",
+ "hs_office_person#anothernewperson:ADMIN",
+ "hs_office_person#anothernewperson:REFERRER"
));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(
- Array.from(
+ Array.fromFormatted(
initialGrantNames,
- "{ grant perm INSERT into hs_office_relation with hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
+ "{ grant perm:hs_office_person#anothernewperson:INSERT>hs_office_relation to role:hs_office_person#anothernewperson:ADMIN by system and assume }",
- "{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by hs_office_person#anothernewperson.owner and assume }",
- "{ grant role hs_office_person#anothernewperson.owner to role global#global.admin by system and assume }",
- "{ grant perm UPDATE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.admin by system and assume }",
- "{ grant perm DELETE on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
- "{ grant role hs_office_person#anothernewperson.admin to role hs_office_person#anothernewperson.owner by system and assume }",
+ "{ grant role:hs_office_person#anothernewperson:OWNER to user:selfregistered-user-drew@hostsharing.org by hs_office_person#anothernewperson:OWNER and assume }",
+ "{ grant role:hs_office_person#anothernewperson:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant perm:hs_office_person#anothernewperson:UPDATE to role:hs_office_person#anothernewperson:ADMIN by system and assume }",
+ "{ grant perm:hs_office_person#anothernewperson:DELETE to role:hs_office_person#anothernewperson:OWNER by system and assume }",
+ "{ grant role:hs_office_person#anothernewperson:ADMIN to role:hs_office_person#anothernewperson:OWNER by system and assume }",
- "{ grant perm SELECT on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.referrer by system and assume }",
- "{ grant role hs_office_person#anothernewperson.referrer to role hs_office_person#anothernewperson.admin by system and assume }"
+ "{ grant perm:hs_office_person#anothernewperson:SELECT to role:hs_office_person#anothernewperson:REFERRER by system and assume }",
+ "{ grant role:hs_office_person#anothernewperson:REFERRER to role:hs_office_person#anothernewperson:ADMIN by system and assume }"
));
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
index 58ad8ae7..f474de0c 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationRepositoryIntegrationTest.java
@@ -125,35 +125,35 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
// then
assertThat(distinctRoleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner",
- "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin",
- "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent",
- "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant"));
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT",
+ "hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// TODO: this grant should only be created for DEBITOR-Relationships, thus the RBAC DSL needs to support conditional grants
- "{ grant perm INSERT into hs_office_sepamandate with hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
+ "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:INSERT>hs_office_sepamandate to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
- "{ grant perm DELETE on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to role global#global.admin by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to user superuser-alex@hostsharing.net by hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner and assume }",
+ "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:DELETE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER to user:superuser-alex@hostsharing.net by hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER and assume }",
- "{ grant perm UPDATE on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_person#ErbenBesslerMelBessler.admin by system and assume }",
+ "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:UPDATE to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:OWNER by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN to role:hs_office_person#ErbenBesslerMelBessler:ADMIN by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_person#BesslerBert.admin by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:ADMIN by system and assume }",
- "{ grant perm SELECT on hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent by system and assume }",
- "{ grant role hs_office_person#BesslerBert.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
- "{ grant role hs_office_person#ErbenBesslerMelBessler.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
- "{ grant role hs_office_contact#fourthcontact.referrer to role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
+ "{ grant perm:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:SELECT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:AGENT by system and assume }",
+ "{ grant role:hs_office_person#BesslerBert:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
+ "{ grant role:hs_office_person#ErbenBesslerMelBessler:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
+ "{ grant role:hs_office_contact#fourthcontact:REFERRER to role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT by system and assume }",
// REPRESENTATIVE holder person -> (represented) anchor person
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_contact#fourthcontact.admin by system and assume }",
- "{ grant role hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_person#BesslerBert.admin by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_contact#fourthcontact:ADMIN by system and assume }",
+ "{ grant role:hs_office_relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert:TENANT to role:hs_office_person#BesslerBert:ADMIN by system and assume }",
null)
);
@@ -219,7 +219,7 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"Bert", "fifth contact");
assertThatRelationIsVisibleForUserWithRole(
givenRelation,
- "hs_office_person#ErbenBesslerMelBessler.admin");
+ "hs_office_person#ErbenBesslerMelBessler:ADMIN");
assertThatRelationActuallyInDatabase(givenRelation);
context("superuser-alex@hostsharing.net");
final var givenContact = contactRepo.findContactByOptionalLabelLike("sixth contact").stream().findFirst().orElseThrow();
@@ -236,14 +236,14 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
assertThat(result.returnedValue().getContact().getLabel()).isEqualTo("sixth contact");
assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
- "global#global.admin");
+ "global#global:ADMIN");
assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_contact#sixthcontact.admin");
+ "hs_office_contact#sixthcontact:ADMIN");
assertThatRelationIsNotVisibleForUserWithRole(
result.returnedValue(),
- "hs_office_contact#fifthcontact.admin");
+ "hs_office_contact#fifthcontact:ADMIN");
relationRepo.deleteByUuid(givenRelation.getUuid());
}
@@ -256,12 +256,12 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"Anita", "eighth");
assertThatRelationIsVisibleForUserWithRole(
givenRelation,
- "hs_office_person#BesslerAnita.admin");
+ "hs_office_person#BesslerAnita:ADMIN");
assertThatRelationActuallyInDatabase(givenRelation);
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_person#BesslerAnita:ADMIN");
givenRelation.setContact(null);
return relationRepo.save(givenRelation);
});
@@ -279,12 +279,12 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
"Anita", "ninth");
assertThatRelationIsVisibleForUserWithRole(
givenRelation,
- "hs_office_contact#ninthcontact.admin");
+ "hs_office_contact#ninthcontact:ADMIN");
assertThatRelationActuallyInDatabase(givenRelation);
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact:ADMIN");
givenRelation.setContact(null); // TODO
return relationRepo.save(givenRelation);
});
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
index 9ffa28f2..a0555579 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
@@ -117,35 +117,35 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
final var all = rawRoleRepo.findAll();
assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
- "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin",
- "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent",
- "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner",
- "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer"));
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):ADMIN",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):AGENT",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER",
+ "hs_office_sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER"));
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(fromFormatted(
initialGrantNames,
// owner
- "{ grant perm DELETE on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner to role global#global.admin by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner to user superuser-alex@hostsharing.net by sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner and assume }",
+ "{ grant perm:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):DELETE to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER to role:global#global:ADMIN by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER to user:superuser-alex@hostsharing.net by sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER and assume }",
// admin
- "{ grant perm UPDATE on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).owner by system and assume }",
+ "{ grant perm:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):UPDATE to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):ADMIN by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):ADMIN to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):OWNER by system and assume }",
// agent
- "{ grant role bankaccount#DE02600501010002034304.referrer to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FirstGmbH.agent to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
+ "{ grant role:bankaccount#DE02600501010002034304:REFERRER to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):AGENT by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):AGENT to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):AGENT by system and assume }",
// referrer
- "{ grant perm SELECT on sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01) to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).agent by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role bankaccount#DE02600501010002034304.admin by system and assume }",
- "{ grant role relation#FirstGmbH-with-DEBITOR-FirstGmbH.tenant to role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer by system and assume }",
- "{ grant role sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01).referrer to role relation#FirstGmbH-with-DEBITOR-FirstGmbH.agent by system and assume }",
+ "{ grant perm:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):SELECT to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):AGENT by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER to role:bankaccount#DE02600501010002034304:ADMIN by system and assume }",
+ "{ grant role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:TENANT to role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER by system and assume }",
+ "{ grant role:sepamandate#DE02600501010002034304-[2020-01-01,2023-01-01):REFERRER to role:relation#FirstGmbH-with-DEBITOR-FirstGmbH:AGENT by system and assume }",
null));
}
@@ -233,7 +233,7 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02600501010002034304");
assertThatSepaMandateIsVisibleForUserWithRole(
givenSepaMandate,
- "hs_office_bankaccount#DE02600501010002034304.admin");
+ "hs_office_bankaccount#DE02600501010002034304:ADMIN");
// when
final var result = jpaAttempt.transacted(() -> {
@@ -262,13 +262,13 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
final var givenSepaMandate = givenSomeTemporarySepaMandate("DE02300606010002474689");
assertThatSepaMandateIsVisibleForUserWithRole(
givenSepaMandate,
- "hs_office_bankaccount#DE02300606010002474689.admin");
+ "hs_office_bankaccount#DE02300606010002474689:ADMIN");
assertThatSepaMandateActuallyInDatabase(givenSepaMandate);
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
- context("superuser-alex@hostsharing.net", "hs_office_bankaccount#DE02300606010002474689.admin");
+ context("superuser-alex@hostsharing.net", "hs_office_bankaccount#DE02300606010002474689:ADMIN");
givenSepaMandate.setValidity(Range.closedOpen(
givenSepaMandate.getValidity().lower(), newValidityEnd));
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
index f56baf34..15738504 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantControllerAcceptanceTest.java
@@ -74,37 +74,37 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.body("", hasItem(
allOf(
// TODO: should there be a grantedByRole or just a grantedByTrigger?
- hasEntry("grantedByRoleIdName", "test_customer#xxx.owner"),
- hasEntry("grantedRoleIdName", "test_customer#xxx.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#xxx:OWNER"),
+ hasEntry("grantedRoleIdName", "test_customer#xxx:ADMIN"),
hasEntry("granteeUserName", "customer-admin@xxx.example.com")
)
))
.body("", hasItem(
allOf(
// TODO: should there be a grantedByRole or just a grantedByTrigger?
- hasEntry("grantedByRoleIdName", "test_customer#yyy.owner"),
- hasEntry("grantedRoleIdName", "test_customer#yyy.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#yyy:OWNER"),
+ hasEntry("grantedRoleIdName", "test_customer#yyy:ADMIN"),
hasEntry("granteeUserName", "customer-admin@yyy.example.com")
)
))
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "global#global.admin"),
- hasEntry("grantedRoleIdName", "global#global.admin"),
+ hasEntry("grantedByRoleIdName", "global#global:ADMIN"),
+ hasEntry("grantedRoleIdName", "global#global:ADMIN"),
hasEntry("granteeUserName", "superuser-fran@hostsharing.net")
)
))
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "test_customer#xxx.admin"),
- hasEntry("grantedRoleIdName", "test_package#xxx00.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#xxx:ADMIN"),
+ hasEntry("grantedRoleIdName", "test_package#xxx00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-xxx00@xxx.example.com")
)
))
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "test_customer#zzz.admin"),
- hasEntry("grantedRoleIdName", "test_package#zzz02.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#zzz:ADMIN"),
+ hasEntry("grantedRoleIdName", "test_package#zzz02:ADMIN"),
hasEntry("granteeUserName", "pac-admin-zzz02@zzz.example.com")
)
))
@@ -118,7 +118,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_package#yyy00.admin")
+ .header("assumed-roles", "test_package#yyy00:ADMIN")
.port(port)
.when()
.get("http://localhost/api/rbac/grants")
@@ -127,8 +127,8 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "test_customer#yyy.admin"),
- hasEntry("grantedRoleIdName", "test_package#yyy00.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"),
+ hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com")
)
))
@@ -150,13 +150,13 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("grantedByRoleIdName", "test_customer#yyy.admin"),
- hasEntry("grantedRoleIdName", "test_package#yyy00.admin"),
+ hasEntry("grantedByRoleIdName", "test_customer#yyy:ADMIN"),
+ hasEntry("grantedRoleIdName", "test_package#yyy00:ADMIN"),
hasEntry("granteeUserName", "pac-admin-yyy00@yyy.example.com")
)
))
- .body("[0].grantedByRoleIdName", is("test_customer#yyy.admin"))
- .body("[0].grantedRoleIdName", is("test_package#yyy00.admin"))
+ .body("[0].grantedByRoleIdName", is("test_customer#yyy:ADMIN"))
+ .body("[0].grantedRoleIdName", is("test_package#yyy00:ADMIN"))
.body("[0].granteeUserName", is("pac-admin-yyy00@yyy.example.com"));
// @formatter:on
}
@@ -171,7 +171,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenCurrentUserAsPackageAdmin = new Subject("customer-admin@xxx.example.com");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
- final var givenGrantedRole = findRbacRoleByName("test_package#xxx00.admin");
+ final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
@@ -180,8 +180,8 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// then
grant.assertThat()
.statusCode(200)
- .body("grantedByRoleIdName", is("test_customer#xxx.admin"))
- .body("grantedRoleIdName", is("test_package#xxx00.admin"))
+ .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
+ .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com"));
}
@@ -191,7 +191,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenCurrentUserAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
- final var givenGrantedRole = findRbacRoleByName("test_package#xxx00.admin");
+ final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
@@ -200,8 +200,8 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// then
grant.assertThat()
.statusCode(200)
- .body("grantedByRoleIdName", is("test_customer#xxx.admin"))
- .body("grantedRoleIdName", is("test_package#xxx00.admin"))
+ .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
+ .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com"));
}
@@ -211,9 +211,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenCurrentUserAsPackageAdmin = new Subject(
"pac-admin-xxx00@xxx.example.com",
- "test_package#xxx00.admin");
+ "test_package#xxx00:ADMIN");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
- final var givenGrantedRole = findRbacRoleByName("test_package#xxx00.admin");
+ final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// when
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
@@ -222,8 +222,8 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// then
grant.assertThat()
.statusCode(200)
- .body("grantedByRoleIdName", is("test_customer#xxx.admin"))
- .body("grantedRoleIdName", is("test_package#xxx00.admin"))
+ .body("grantedByRoleIdName", is("test_customer#xxx:ADMIN"))
+ .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is("pac-admin-xxx00@xxx.example.com"));
}
@@ -234,9 +234,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenCurrentUserAsPackageAdmin = new Subject(
"pac-admin-xxx00@xxx.example.com",
- "test_package#xxx00.tenant");
+ "test_package#xxx00:TENANT");
final var givenGranteeUser = findRbacUserByName("pac-admin-xxx00@xxx.example.com");
- final var givenGrantedRole = findRbacRoleByName("test_package#xxx00.admin");
+ final var givenGrantedRole = getRbacRoleByName("test_package#xxx00:ADMIN");
final var grant = givenCurrentUserAsPackageAdmin.getGrantById()
.forGrantedRole(givenGrantedRole).toGranteeUser(givenGranteeUser);
@@ -255,10 +255,10 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenNewUser = createRBacUser();
- final var givenRoleToGrant = "test_package#xxx00.admin";
+ final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givenCurrentUserAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
final var givenOwnPackageAdminRole =
- findRbacRoleByName(givenCurrentUserAsPackageAdmin.assumedRole);
+ getRbacRoleByName(givenCurrentUserAsPackageAdmin.assumedRole);
// when
final var response = givenCurrentUserAsPackageAdmin
@@ -268,15 +268,15 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// then
response.assertThat()
.statusCode(201)
- .body("grantedByRoleIdName", is("test_package#xxx00.admin"))
+ .body("grantedByRoleIdName", is("test_package#xxx00:ADMIN"))
.body("assumed", is(true))
- .body("grantedRoleIdName", is("test_package#xxx00.admin"))
+ .body("grantedRoleIdName", is("test_package#xxx00:ADMIN"))
.body("granteeUserName", is(givenNewUser.getName()));
assertThat(findAllGrantsOf(givenCurrentUserAsPackageAdmin))
.extracting(RbacGrantEntity::toDisplay)
- .contains("{ grant role " + givenOwnPackageAdminRole.getRoleName() +
- " to user " + givenNewUser.getName() +
- " by role " + givenRoleToGrant + " and assume }");
+ .contains("{ grant role:" + givenOwnPackageAdminRole.getRoleName() +
+ " to user:" + givenNewUser.getName() +
+ " by role:" + givenRoleToGrant + " and assume }");
}
@Test
@@ -285,9 +285,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenNewUser = createRBacUser();
- final var givenRoleToGrant = "test_package#xxx00.admin";
+ final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givenCurrentUserAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
- final var givenAlienPackageAdminRole = findRbacRoleByName("test_package#yyy00.admin");
+ final var givenAlienPackageAdminRole = getRbacRoleByName("test_package#yyy00:ADMIN");
// when
final var result = givenCurrentUserAsPackageAdmin
@@ -298,7 +298,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
result.assertThat()
.statusCode(403)
.body("message", containsString("Access to granted role"))
- .body("message", containsString("forbidden for test_package#xxx00.admin"));
+ .body("message", containsString("forbidden for test_package#xxx00:ADMIN"));
assertThat(findAllGrantsOf(givenCurrentUserAsPackageAdmin))
.extracting(RbacGrantEntity::getGranteeUserName)
.doesNotContain(givenNewUser.getName());
@@ -315,9 +315,9 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
// given
final var givenArbitraryUser = createRBacUser();
- final var givenRoleToGrant = "test_package#xxx00.admin";
+ final var givenRoleToGrant = "test_package#xxx00:ADMIN";
final var givenCurrentUserAsPackageAdmin = new Subject("pac-admin-xxx00@xxx.example.com", givenRoleToGrant);
- final var givenOwnPackageAdminRole = findRbacRoleByName("test_package#xxx00.admin");
+ final var givenOwnPackageAdminRole = getRbacRoleByName("test_package#xxx00:ADMIN");
// and given an existing grant
assumeCreated(givenCurrentUserAsPackageAdmin
@@ -325,7 +325,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
.toUser(givenArbitraryUser));
assumeGrantExists(
givenCurrentUserAsPackageAdmin,
- "{ grant role %s to user %s by role %s and assume }".formatted(
+ "{ grant role:%s to user:%s by role:%s and assume }".formatted(
givenOwnPackageAdminRole.getRoleName(),
givenArbitraryUser.getName(),
givenCurrentUserAsPackageAdmin.assumedRole));
@@ -504,13 +504,13 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
return rbacUserRepository.findByName(userName);
- }).returnedValue();
+ }).assertNotNull().returnedValue();
}
- RbacRoleEntity findRbacRoleByName(final String roleName) {
+ RbacRoleEntity getRbacRoleByName(final String roleName) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
return rbacRoleRepository.findByRoleName(roleName);
- }).returnedValue();
+ }).assertNotNull().returnedValue();
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntityUnitTest.java
index eea18932..c0bd82cc 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantEntityUnitTest.java
@@ -34,13 +34,13 @@ class RbacGrantEntityUnitTest {
"GrantEE", UUID.randomUUID(),
true,
"ObjectTable", "ObjectId", UUID.randomUUID(),
- RbacRoleType.admin); // @formatter:on
+ RbacRoleType.ADMIN); // @formatter:on
// when
final var display = entity.toDisplay();
// then
- assertThat(display).isEqualTo("{ grant role GrantED to user GrantEE by role GrantER and assume }");
+ assertThat(display).isEqualTo("{ grant role:GrantED to user:GrantEE by role:GrantER and assume }");
}
@Test
@@ -52,12 +52,12 @@ class RbacGrantEntityUnitTest {
"GrantEE", UUID.randomUUID(),
false,
"ObjectTable", "ObjectId", UUID.randomUUID(),
- RbacRoleType.owner); // @formatter:on
+ RbacRoleType.OWNER); // @formatter:on
// when
final var display = entity.toDisplay();
// then
- assertThat(display).isEqualTo("{ grant role GrantED to user GrantEE by role GrantER }");
+ assertThat(display).isEqualTo("{ grant role:GrantED to user:GrantEE by role:GrantER }");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
index 8ce615b7..0ee1f297 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantRepositoryIntegrationTest.java
@@ -69,7 +69,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseRbacGrantsAreReturned(
result,
- "{ grant role test_package#xxx00.admin to user pac-admin-xxx00@xxx.example.com by role test_customer#xxx.admin and assume }");
+ "{ grant role:test_package#xxx00:ADMIN to user:pac-admin-xxx00@xxx.example.com by role:test_customer#xxx:ADMIN and assume }");
}
@Test
@@ -84,17 +84,17 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseRbacGrantsAreReturned(
result,
- "{ grant role test_customer#xxx.admin to user customer-admin@xxx.example.com by role test_customer#xxx.owner and assume }",
- "{ grant role test_package#xxx00.admin to user pac-admin-xxx00@xxx.example.com by role test_customer#xxx.admin and assume }",
- "{ grant role test_package#xxx01.admin to user pac-admin-xxx01@xxx.example.com by role test_customer#xxx.admin and assume }",
- "{ grant role test_package#xxx02.admin to user pac-admin-xxx02@xxx.example.com by role test_customer#xxx.admin and assume }");
+ "{ grant role:test_customer#xxx:ADMIN to user:customer-admin@xxx.example.com by role:test_customer#xxx:OWNER and assume }",
+ "{ grant role:test_package#xxx00:ADMIN to user:pac-admin-xxx00@xxx.example.com by role:test_customer#xxx:ADMIN and assume }",
+ "{ grant role:test_package#xxx01:ADMIN to user:pac-admin-xxx01@xxx.example.com by role:test_customer#xxx:ADMIN and assume }",
+ "{ grant role:test_package#xxx02:ADMIN to user:pac-admin-xxx02@xxx.example.com by role:test_customer#xxx:ADMIN and assume }");
}
@Test
@Accepts({ "GRT:L(List)" })
public void customerAdmin_withAssumedRole_canOnlyViewRbacGrantsVisibleByAssumedRole() {
// given:
- context("customer-admin@xxx.example.com", "test_package#xxx00.admin");
+ context("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
// when
final var result = rbacGrantRepository.findAll();
@@ -102,7 +102,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseRbacGrantsAreReturned(
result,
- "{ grant role test_package#xxx00.admin to user pac-admin-xxx00@xxx.example.com by role test_customer#xxx.admin and assume }");
+ "{ grant role:test_package#xxx00:ADMIN to user:pac-admin-xxx00@xxx.example.com by role:test_customer#xxx:ADMIN and assume }");
}
}
@@ -112,9 +112,9 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void customerAdmin_canGrantOwnPackageAdminRole_toArbitraryUser() {
// given
- context("customer-admin@xxx.example.com", "test_customer#xxx.admin");
+ context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
final var givenArbitraryUserUuid = rbacUserRepository.findByName("pac-admin-zzz00@zzz.example.com").getUuid();
- final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName("test_package#xxx00.admin").getUuid();
+ final var givenOwnPackageRoleUuid = rbacRoleRepository.findByRoleName("test_package#xxx00:ADMIN").getUuid();
// when
final var grant = RbacGrantEntity.builder()
@@ -130,7 +130,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
assertThat(rbacGrantRepository.findAll())
.extracting(RbacGrantEntity::toDisplay)
.contains(
- "{ grant role test_package#xxx00.admin to user pac-admin-zzz00@zzz.example.com by role test_customer#xxx.admin and assume }");
+ "{ grant role:test_package#xxx00:ADMIN to user:pac-admin-zzz00@zzz.example.com by role:test_customer#xxx:ADMIN and assume }");
}
@Test
@@ -143,14 +143,14 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
context("customer-admin@xxx.example.com", null);
return new Given(
createNewUser(),
- rbacRoleRepository.findByRoleName("test_package#xxx00.owner").getUuid()
+ rbacRoleRepository.findByRoleName("test_package#xxx00:OWNER").getUuid()
);
}).assumeSuccessful().returnedValue();
// when
final var attempt = jpaAttempt.transacted(() -> {
// now we try to use these uuids as a less privileged user
- context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00.admin");
+ context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00:ADMIN");
final var grant = RbacGrantEntity.builder()
.granteeUserUuid(given.arbitraryUser.getUuid())
.grantedRoleUuid(given.packageOwnerRoleUuid)
@@ -162,8 +162,8 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// then
attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
- "ERROR: [403] Access to granted role test_package#xxx00.owner",
- "forbidden for test_package#xxx00.admin");
+ "ERROR: [403] Access to granted role test_package#xxx00:OWNER",
+ "forbidden for test_package#xxx00:ADMIN");
jpaAttempt.transacted(() -> {
// finally, we use the new user to make sure, no roles were granted
context(given.arbitraryUser.getName(), null);
@@ -180,16 +180,16 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
public void customerAdmin_canRevokeSelfGrantedPackageAdminRole() {
// given
final var grant = create(grant()
- .byUser("customer-admin@xxx.example.com").withAssumedRole("test_customer#xxx.admin")
- .grantingRole("test_package#xxx00.admin").toUser("pac-admin-zzz00@zzz.example.com"));
+ .byUser("customer-admin@xxx.example.com").withAssumedRole("test_customer#xxx:ADMIN")
+ .grantingRole("test_package#xxx00:ADMIN").toUser("pac-admin-zzz00@zzz.example.com"));
// when
- context("customer-admin@xxx.example.com", "test_customer#xxx.admin");
+ context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
final var revokeAttempt = attempt(em, () ->
rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()));
// then
- context("customer-admin@xxx.example.com", "test_customer#xxx.admin");
+ context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull();
assertThat(rbacGrantRepository.findAll())
.extracting(RbacGrantEntity::getGranteeUserName)
@@ -201,17 +201,17 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
// given
final var newUser = createNewUserTransacted();
final var grant = create(grant()
- .byUser("customer-admin@xxx.example.com").withAssumedRole("test_package#xxx00.admin")
- .grantingRole("test_package#xxx00.admin").toUser(newUser.getName()));
+ .byUser("customer-admin@xxx.example.com").withAssumedRole("test_package#xxx00:ADMIN")
+ .grantingRole("test_package#xxx00:ADMIN").toUser(newUser.getName()));
// when
- context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00.admin");
+ context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00:ADMIN");
final var revokeAttempt = attempt(em, () ->
rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()));
// then
assertThat(revokeAttempt.caughtExceptionsRootCause()).isNull();
- context("customer-admin@xxx.example.com", "test_customer#xxx.admin");
+ context("customer-admin@xxx.example.com", "test_customer#xxx:ADMIN");
assertThat(rbacGrantRepository.findAll())
.extracting(RbacGrantEntity::getGranteeUserName)
.doesNotContain("pac-admin-zzz00@zzz.example.com");
@@ -221,19 +221,19 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
public void packageAdmin_canNotRevokeOwnPackageAdminRoleGrantedByOwnerRoleOfThatPackage() {
// given
final var grant = create(grant()
- .byUser("customer-admin@xxx.example.com").withAssumedRole("test_package#xxx00.owner")
- .grantingRole("test_package#xxx00.admin").toUser("pac-admin-zzz00@zzz.example.com"));
- final var grantedByRole = rbacRoleRepository.findByRoleName("test_package#xxx00.owner");
+ .byUser("customer-admin@xxx.example.com").withAssumedRole("test_package#xxx00:OWNER")
+ .grantingRole("test_package#xxx00:ADMIN").toUser("pac-admin-zzz00@zzz.example.com"));
+ final var grantedByRole = rbacRoleRepository.findByRoleName("test_package#xxx00:OWNER");
// when
- context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00.admin");
+ context("pac-admin-xxx00@xxx.example.com", "test_package#xxx00:ADMIN");
final var revokeAttempt = attempt(em, () ->
rbacGrantRepository.deleteByRbacGrantId(grant.getRbacGrantId()));
// then
revokeAttempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class,
- "ERROR: [403] Revoking role created by %s is forbidden for {test_package#xxx00.admin}.".formatted(
+ "ERROR: [403] Revoking role created by %s is forbidden for {test_package#xxx00:ADMIN}.".formatted(
grantedByRole.getUuid()
));
}
@@ -254,7 +254,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
assertThat(grantAttempt.caughtException()).isNull();
assertThat(rawRbacGrantRepository.findAll())
.extracting(RawRbacGrantEntity::toDisplay)
- .contains("{ grant role %s to user %s by %s and assume }".formatted(
+ .contains("{ grant role:%s to user:%s by %s and assume }".formatted(
with.grantedRole, with.granteeUserName, with.assumedRole
));
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
index 0e0421c8..5d228314 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacgrant/RbacGrantsDiagramServiceIntegrationTest.java
@@ -54,43 +54,43 @@ class RbacGrantsDiagramServiceIntegrationTest extends ContextBasedTestWithCleanu
@Test
void allGrantsToCurrentUser() {
- context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
+ context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa:OWNER");
final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES));
assertThat(graph).isEqualTo("""
flowchart TB
- role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
- role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
- role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
- role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
+ role:test_domain#xxx00-aaaa:ADMIN --> role:test_package#xxx00:TENANT
+ role:test_domain#xxx00-aaaa:OWNER --> role:test_domain#xxx00-aaaa:ADMIN
+ role:test_domain#xxx00-aaaa:OWNER --> role:test_package#xxx00:TENANT
+ role:test_package#xxx00:TENANT --> role:test_customer#xxx:TENANT
""".trim());
}
@Test
void allGrantsToCurrentUserIncludingPermissions() {
- context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa.owner");
+ context("superuser-alex@hostsharing.net", "test_domain#xxx00-aaaa:OWNER");
final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.TEST_ENTITIES, Include.PERMISSIONS));
assertThat(graph).isEqualTo("""
flowchart TB
- role:test_customer#xxx.tenant --> perm:SELECT:on:test_customer#xxx
- role:test_domain#xxx00-aaaa.admin --> perm:SELECT:on:test_domain#xxx00-aaaa
- role:test_domain#xxx00-aaaa.admin --> role:test_package#xxx00.tenant
- role:test_domain#xxx00-aaaa.owner --> perm:DELETE:on:test_domain#xxx00-aaaa
- role:test_domain#xxx00-aaaa.owner --> perm:UPDATE:on:test_domain#xxx00-aaaa
- role:test_domain#xxx00-aaaa.owner --> role:test_domain#xxx00-aaaa.admin
- role:test_domain#xxx00-aaaa.owner --> role:test_package#xxx00.tenant
- role:test_package#xxx00.tenant --> perm:SELECT:on:test_package#xxx00
- role:test_package#xxx00.tenant --> role:test_customer#xxx.tenant
+ role:test_customer#xxx:TENANT --> perm:test_customer#xxx:SELECT
+ role:test_domain#xxx00-aaaa:ADMIN --> perm:test_domain#xxx00-aaaa:SELECT
+ role:test_domain#xxx00-aaaa:ADMIN --> role:test_package#xxx00:TENANT
+ role:test_domain#xxx00-aaaa:OWNER --> perm:test_domain#xxx00-aaaa:DELETE
+ role:test_domain#xxx00-aaaa:OWNER --> perm:test_domain#xxx00-aaaa:UPDATE
+ role:test_domain#xxx00-aaaa:OWNER --> role:test_domain#xxx00-aaaa:ADMIN
+ role:test_domain#xxx00-aaaa:OWNER --> role:test_package#xxx00:TENANT
+ role:test_package#xxx00:TENANT --> perm:test_package#xxx00:SELECT
+ role:test_package#xxx00:TENANT --> role:test_customer#xxx:TENANT
""".trim());
}
@Test
@Disabled // enable to generate from a real database
void print() throws IOException {
- //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan.admin");
+ //context("superuser-alex@hostsharing.net", "hs_office_person#FirbySusan:ADMIN");
context("superuser-alex@hostsharing.net");
//final var graph = grantsMermaidService.allGrantsToCurrentUser(EnumSet.of(Include.NON_TEST_ENTITIES, Include.PERMISSIONS));
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java
index 2f4d15f5..e80f8ce6 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RawRbacRoleEntity.java
@@ -35,7 +35,7 @@ public class RawRbacRoleEntity {
@Enumerated(EnumType.STRING)
private RbacRoleType roleType;
- @Formula("objectTable||'#'||objectIdName||'.'||roleType")
+ @Formula("objectTable||'#'||objectIdName||':'||roleType")
private String roleName;
@NotNull
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java
index 5de93348..d318cc04 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java
@@ -45,14 +45,14 @@ class RbacRoleControllerAcceptanceTest {
.then().assertThat()
.statusCode(200)
.contentType("application/json")
- .body("", hasItem(hasEntry("roleName", "test_customer#xxx.admin")))
- .body("", hasItem(hasEntry("roleName", "test_customer#xxx.owner")))
- .body("", hasItem(hasEntry("roleName", "test_customer#xxx.tenant")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#xxx:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#xxx:OWNER")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#xxx:TENANT")))
// ...
- .body("", hasItem(hasEntry("roleName", "global#global.admin")))
- .body("", hasItem(hasEntry("roleName", "test_customer#yyy.admin")))
- .body("", hasItem(hasEntry("roleName", "test_package#yyy00.admin")))
- .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa.owner")))
+ .body("", hasItem(hasEntry("roleName", "global#global:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#yyy:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_package#yyy00:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa:OWNER")))
.body( "size()", greaterThanOrEqualTo(73)); // increases with new test data
// @formatter:on
}
@@ -65,7 +65,7 @@ class RbacRoleControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_package#yyy00.admin")
+ .header("assumed-roles", "test_package#yyy00:ADMIN")
.port(port)
.when()
.get("http://localhost/api/rbac/roles")
@@ -75,18 +75,18 @@ class RbacRoleControllerAcceptanceTest {
.statusCode(200)
.contentType("application/json")
- .body("", hasItem(hasEntry("roleName", "test_customer#yyy.tenant")))
- .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa.owner")))
- .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa.admin")))
- .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaab.owner")))
- .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaab.admin")))
- .body("", hasItem(hasEntry("roleName", "test_package#yyy00.admin")))
- .body("", hasItem(hasEntry("roleName", "test_package#yyy00.tenant")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#yyy:TENANT")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa:OWNER")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaab:OWNER")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#yyy00-aaab:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_package#yyy00:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_package#yyy00:TENANT")))
- .body("", not(hasItem(hasEntry("roleName", "test_customer#xxx.tenant"))))
- .body("", not(hasItem(hasEntry("roleName", "test_domain#xxx00-aaaa.admin"))))
- .body("", not(hasItem(hasEntry("roleName", "test_package#xxx00.admin"))))
- .body("", not(hasItem(hasEntry("roleName", "test_package#xxx00.tenant"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_customer#xxx:TENANT"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_domain#xxx00-aaaa:ADMIN"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_package#xxx00:ADMIN"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_package#xxx00:TENANT"))))
;
// @formatter:on
}
@@ -106,15 +106,15 @@ class RbacRoleControllerAcceptanceTest {
.statusCode(200)
.contentType("application/json")
- .body("", hasItem(hasEntry("roleName", "test_customer#zzz.tenant")))
- .body("", hasItem(hasEntry("roleName", "test_domain#zzz00-aaaa.admin")))
- .body("", hasItem(hasEntry("roleName", "test_package#zzz00.admin")))
- .body("", hasItem(hasEntry("roleName", "test_package#zzz00.tenant")))
+ .body("", hasItem(hasEntry("roleName", "test_customer#zzz:TENANT")))
+ .body("", hasItem(hasEntry("roleName", "test_domain#zzz00-aaaa:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_package#zzz00:ADMIN")))
+ .body("", hasItem(hasEntry("roleName", "test_package#zzz00:TENANT")))
- .body("", not(hasItem(hasEntry("roleName", "test_customer#yyy.tenant"))))
- .body("", not(hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa.admin"))))
- .body("", not(hasItem(hasEntry("roleName", "test_package#yyy00.admin"))))
- .body("", not(hasItem(hasEntry("roleName", "test_package#yyy00.tenant"))));
+ .body("", not(hasItem(hasEntry("roleName", "test_customer#yyy:TENANT"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_domain#yyy00-aaaa:ADMIN"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_package#yyy00:ADMIN"))))
+ .body("", not(hasItem(hasEntry("roleName", "test_package#yyy00:TENANT"))));
// @formatter:on
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java
index c10a9cbc..44b3885e 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerRestTest.java
@@ -73,9 +73,9 @@ class RbacRoleControllerRestTest {
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(3)))
- .andExpect(jsonPath("$[0].roleName", is("global#global.admin")))
- .andExpect(jsonPath("$[1].roleName", is("test_customer#xxx.owner")))
- .andExpect(jsonPath("$[2].roleName", is("test_customer#xxx.admin")))
+ .andExpect(jsonPath("$[0].roleName", is("global#global:ADMIN")))
+ .andExpect(jsonPath("$[1].roleName", is("test_customer#xxx:OWNER")))
+ .andExpect(jsonPath("$[2].roleName", is("test_customer#xxx:ADMIN")))
.andExpect(jsonPath("$[2].uuid", is(customerXxxAdmin.getUuid().toString())))
.andExpect(jsonPath("$[2].objectUuid", is(customerXxxAdmin.getObjectUuid().toString())))
.andExpect(jsonPath("$[2].objectTable", is(customerXxxAdmin.getObjectTable().toString())))
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java
index 197e0bc0..4d873fa6 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleRepositoryIntegrationTest.java
@@ -39,19 +39,19 @@ class RbacRoleRepositoryIntegrationTest {
private static final String[] ALL_TEST_DATA_ROLES = Array.of(
// @formatter:off
- "global#global.admin",
- "test_customer#xxx.admin", "test_customer#xxx.owner", "test_customer#xxx.tenant",
- "test_package#xxx00.admin", "test_package#xxx00.owner", "test_package#xxx00.tenant",
- "test_package#xxx01.admin", "test_package#xxx01.owner", "test_package#xxx01.tenant",
- "test_package#xxx02.admin", "test_package#xxx02.owner", "test_package#xxx02.tenant",
- "test_customer#yyy.admin", "test_customer#yyy.owner", "test_customer#yyy.tenant",
- "test_package#yyy00.admin", "test_package#yyy00.owner", "test_package#yyy00.tenant",
- "test_package#yyy01.admin", "test_package#yyy01.owner", "test_package#yyy01.tenant",
- "test_package#yyy02.admin", "test_package#yyy02.owner", "test_package#yyy02.tenant",
- "test_customer#zzz.admin", "test_customer#zzz.owner", "test_customer#zzz.tenant",
- "test_package#zzz00.admin", "test_package#zzz00.owner", "test_package#zzz00.tenant",
- "test_package#zzz01.admin", "test_package#zzz01.owner", "test_package#zzz01.tenant",
- "test_package#zzz02.admin", "test_package#zzz02.owner", "test_package#zzz02.tenant"
+ "global#global:ADMIN",
+ "test_customer#xxx:ADMIN", "test_customer#xxx:OWNER", "test_customer#xxx:TENANT",
+ "test_package#xxx00:ADMIN", "test_package#xxx00:OWNER", "test_package#xxx00:TENANT",
+ "test_package#xxx01:ADMIN", "test_package#xxx01:OWNER", "test_package#xxx01:TENANT",
+ "test_package#xxx02:ADMIN", "test_package#xxx02:OWNER", "test_package#xxx02:TENANT",
+ "test_customer#yyy:ADMIN", "test_customer#yyy:OWNER", "test_customer#yyy:TENANT",
+ "test_package#yyy00:ADMIN", "test_package#yyy00:OWNER", "test_package#yyy00:TENANT",
+ "test_package#yyy01:ADMIN", "test_package#yyy01:OWNER", "test_package#yyy01:TENANT",
+ "test_package#yyy02:ADMIN", "test_package#yyy02:OWNER", "test_package#yyy02:TENANT",
+ "test_customer#zzz:ADMIN", "test_customer#zzz:OWNER", "test_customer#zzz:TENANT",
+ "test_package#zzz00:ADMIN", "test_package#zzz00:OWNER", "test_package#zzz00:TENANT",
+ "test_package#zzz01:ADMIN", "test_package#zzz01:OWNER", "test_package#zzz01:TENANT",
+ "test_package#zzz02:ADMIN", "test_package#zzz02:OWNER", "test_package#zzz02:TENANT"
// @formatter:on
);
@@ -70,7 +70,7 @@ class RbacRoleRepositoryIntegrationTest {
@Test
public void globalAdmin_withAssumedglobalAdminRole_canViewAllRbacRoles() {
given:
- context.define("superuser-alex@hostsharing.net", "global#global.admin");
+ context.define("superuser-alex@hostsharing.net", "global#global:ADMIN");
// when
final var result = rbacRoleRepository.findAll();
@@ -91,49 +91,49 @@ class RbacRoleRepositoryIntegrationTest {
allTheseRbacRolesAreReturned(
result,
// @formatter:off
- "test_customer#xxx.admin",
- "test_customer#xxx.tenant",
- "test_package#xxx00.admin",
- "test_package#xxx00.owner",
- "test_package#xxx00.tenant",
- "test_package#xxx01.admin",
- "test_package#xxx01.owner",
- "test_package#xxx01.tenant",
+ "test_customer#xxx:ADMIN",
+ "test_customer#xxx:TENANT",
+ "test_package#xxx00:ADMIN",
+ "test_package#xxx00:OWNER",
+ "test_package#xxx00:TENANT",
+ "test_package#xxx01:ADMIN",
+ "test_package#xxx01:OWNER",
+ "test_package#xxx01:TENANT",
// ...
- "test_domain#xxx00-aaaa.admin",
- "test_domain#xxx00-aaaa.owner",
+ "test_domain#xxx00-aaaa:ADMIN",
+ "test_domain#xxx00-aaaa:OWNER",
// ..
- "test_domain#xxx01-aaab.admin",
- "test_domain#xxx01-aaab.owner"
+ "test_domain#xxx01-aaab:ADMIN",
+ "test_domain#xxx01-aaab:OWNER"
// @formatter:on
);
noneOfTheseRbacRolesIsReturned(
result,
// @formatter:off
- "global#global.admin",
- "test_customer#xxx.owner",
- "test_package#yyy00.admin",
- "test_package#yyy00.owner",
- "test_package#yyy00.tenant"
+ "global#global:ADMIN",
+ "test_customer#xxx:OWNER",
+ "test_package#yyy00:ADMIN",
+ "test_package#yyy00:OWNER",
+ "test_package#yyy00:TENANT"
// @formatter:on
);
}
@Test
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnRbacRole() {
- context.define("customer-admin@xxx.example.com", "test_package#xxx00.admin");
+ context.define("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
final var result = rbacRoleRepository.findAll();
exactlyTheseRbacRolesAreReturned(
result,
- "test_customer#xxx.tenant",
- "test_package#xxx00.admin",
- "test_package#xxx00.tenant",
- "test_domain#xxx00-aaaa.admin",
- "test_domain#xxx00-aaaa.owner",
- "test_domain#xxx00-aaab.admin",
- "test_domain#xxx00-aaab.owner");
+ "test_customer#xxx:TENANT",
+ "test_package#xxx00:ADMIN",
+ "test_package#xxx00:TENANT",
+ "test_domain#xxx00-aaaa:ADMIN",
+ "test_domain#xxx00-aaaa:OWNER",
+ "test_domain#xxx00-aaab:ADMIN",
+ "test_domain#xxx00-aaab:OWNER");
}
@Test
@@ -157,19 +157,19 @@ class RbacRoleRepositoryIntegrationTest {
void customerAdmin_withoutAssumedRole_canFindItsOwnRolesByName() {
context.define("customer-admin@xxx.example.com");
- final var result = rbacRoleRepository.findByRoleName("test_customer#xxx.admin");
+ final var result = rbacRoleRepository.findByRoleName("test_customer#xxx:ADMIN");
assertThat(result).isNotNull();
assertThat(result.getObjectTable()).isEqualTo("test_customer");
assertThat(result.getObjectIdName()).isEqualTo("xxx");
- assertThat(result.getRoleType()).isEqualTo(RbacRoleType.admin);
+ assertThat(result.getRoleType()).isEqualTo(RbacRoleType.ADMIN);
}
@Test
void customerAdmin_withoutAssumedRole_canNotFindAlienRolesByName() {
context.define("customer-admin@xxx.example.com");
- final var result = rbacRoleRepository.findByRoleName("test_customer#bbb.admin");
+ final var result = rbacRoleRepository.findByRoleName("test_customer#bbb:ADMIN");
assertThat(result).isNull();
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
index 652679f3..73e30a1b 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/TestRbacRole.java
@@ -4,11 +4,11 @@ import static java.util.UUID.randomUUID;
public class TestRbacRole {
- public static final RbacRoleEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.admin);
- static final RbacRoleEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.owner);
- static final RbacRoleEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.admin);
+ public static final RbacRoleEntity hostmasterRole = rbacRole("global", "global", RbacRoleType.ADMIN);
+ static final RbacRoleEntity customerXxxOwner = rbacRole("test_customer", "xxx", RbacRoleType.OWNER);
+ static final RbacRoleEntity customerXxxAdmin = rbacRole("test_customer", "xxx", RbacRoleType.ADMIN);
static public RbacRoleEntity rbacRole(final String objectTable, final String objectIdName, final RbacRoleType roleType) {
- return new RbacRoleEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+'.'+roleType);
+ return new RbacRoleEntity(randomUUID(), randomUUID(), objectTable, objectIdName, roleType, objectTable+'#'+objectIdName+':'+roleType);
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
index 9d7e16ca..6faa28ff 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java
@@ -104,7 +104,7 @@ class RbacUserControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#yyy.admin")
+ .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port)
.when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid())
@@ -210,7 +210,7 @@ class RbacUserControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#yyy.admin")
+ .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port)
.when()
.get("http://localhost/api/rbac/users")
@@ -287,12 +287,12 @@ class RbacUserControllerAcceptanceTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_customer#yyy.tenant"),
+ hasEntry("roleName", "test_customer#yyy:TENANT"),
hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
+ hasEntry("roleName", "test_domain#yyy00-aaaa:OWNER"),
hasEntry("op", "DELETE"))
))
// actual content tested in integration test, so this is enough for here:
@@ -309,7 +309,7 @@ class RbacUserControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#yyy.admin")
+ .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port)
.when()
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions")
@@ -318,12 +318,12 @@ class RbacUserControllerAcceptanceTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_customer#yyy.tenant"),
+ hasEntry("roleName", "test_customer#yyy:TENANT"),
hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
+ hasEntry("roleName", "test_domain#yyy00-aaaa:OWNER"),
hasEntry("op", "DELETE"))
))
// actual content tested in integration test, so this is enough for here:
@@ -348,12 +348,12 @@ class RbacUserControllerAcceptanceTest {
.contentType("application/json")
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_customer#yyy.tenant"),
+ hasEntry("roleName", "test_customer#yyy:TENANT"),
hasEntry("op", "SELECT"))
))
.body("", hasItem(
allOf(
- hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
+ hasEntry("roleName", "test_domain#yyy00-aaaa:OWNER"),
hasEntry("op", "DELETE"))
))
// actual content tested in integration test, so this is enough for here:
diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
index c63047ed..43c8bff1 100644
--- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserRepositoryIntegrationTest.java
@@ -116,7 +116,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void globalAdmin_withAssumedglobalAdminRole_canViewAllRbacUsers() {
given:
- context("superuser-alex@hostsharing.net", "global#global.admin");
+ context("superuser-alex@hostsharing.net", "global#global:ADMIN");
// when
final var result = rbacUserRepository.findByOptionalNameLike(null);
@@ -128,7 +128,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void globalAdmin_withAssumedCustomerAdminRole_canViewOnlyUsersHavingRolesInThatCustomersRealm() {
given:
- context("superuser-alex@hostsharing.net", "test_customer#xxx.admin");
+ context("superuser-alex@hostsharing.net", "test_customer#xxx:ADMIN");
// when
final var result = rbacUserRepository.findByOptionalNameLike(null);
@@ -159,7 +159,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyUsersHavingRolesInThatPackage() {
- context("customer-admin@xxx.example.com", "test_package#xxx00.admin");
+ context("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
final var result = rbacUserRepository.findByOptionalNameLike(null);
@@ -182,47 +182,47 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
private static final String[] ALL_USER_PERMISSIONS = Array.of(
// @formatter:off
- "test_customer#xxx.admin -> test_customer#xxx: SELECT",
- "test_customer#xxx.owner -> test_customer#xxx: DELETE",
- "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
- "test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
- "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
- "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
- "test_package#xxx01.tenant -> test_package#xxx01: SELECT",
- "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
- "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
- "test_package#xxx02.tenant -> test_package#xxx02: SELECT",
+ "test_customer#xxx:ADMIN -> test_customer#xxx: SELECT",
+ "test_customer#xxx:OWNER -> test_customer#xxx: DELETE",
+ "test_customer#xxx:TENANT -> test_customer#xxx: SELECT",
+ "test_customer#xxx:ADMIN -> test_customer#xxx: INSERT:test_package",
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:TENANT -> test_package#xxx00: SELECT",
+ "test_package#xxx01:ADMIN -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01:ADMIN -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01:TENANT -> test_package#xxx01: SELECT",
+ "test_package#xxx02:ADMIN -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02:ADMIN -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02:TENANT -> test_package#xxx02: SELECT",
- "test_customer#yyy.admin -> test_customer#yyy: SELECT",
- "test_customer#yyy.owner -> test_customer#yyy: DELETE",
- "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
- "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
- "test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
- "test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
- "test_package#yyy01.tenant -> test_package#yyy01: SELECT",
- "test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
- "test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
- "test_package#yyy02.tenant -> test_package#yyy02: SELECT",
+ "test_customer#yyy:ADMIN -> test_customer#yyy: SELECT",
+ "test_customer#yyy:OWNER -> test_customer#yyy: DELETE",
+ "test_customer#yyy:TENANT -> test_customer#yyy: SELECT",
+ "test_customer#yyy:ADMIN -> test_customer#yyy: INSERT:test_package",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:TENANT -> test_package#yyy00: SELECT",
+ "test_package#yyy01:ADMIN -> test_package#yyy01: INSERT:test_domain",
+ "test_package#yyy01:ADMIN -> test_package#yyy01: INSERT:test_domain",
+ "test_package#yyy01:TENANT -> test_package#yyy01: SELECT",
+ "test_package#yyy02:ADMIN -> test_package#yyy02: INSERT:test_domain",
+ "test_package#yyy02:ADMIN -> test_package#yyy02: INSERT:test_domain",
+ "test_package#yyy02:TENANT -> test_package#yyy02: SELECT",
- "test_customer#zzz.admin -> test_customer#zzz: SELECT",
- "test_customer#zzz.owner -> test_customer#zzz: DELETE",
- "test_customer#zzz.tenant -> test_customer#zzz: SELECT",
- "test_customer#zzz.admin -> test_customer#zzz: INSERT:test_package",
- "test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
- "test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
- "test_package#zzz00.tenant -> test_package#zzz00: SELECT",
- "test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
- "test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
- "test_package#zzz01.tenant -> test_package#zzz01: SELECT",
- "test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
- "test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
- "test_package#zzz02.tenant -> test_package#zzz02: SELECT"
+ "test_customer#zzz:ADMIN -> test_customer#zzz: SELECT",
+ "test_customer#zzz:OWNER -> test_customer#zzz: DELETE",
+ "test_customer#zzz:TENANT -> test_customer#zzz: SELECT",
+ "test_customer#zzz:ADMIN -> test_customer#zzz: INSERT:test_package",
+ "test_package#zzz00:ADMIN -> test_package#zzz00: INSERT:test_domain",
+ "test_package#zzz00:ADMIN -> test_package#zzz00: INSERT:test_domain",
+ "test_package#zzz00:TENANT -> test_package#zzz00: SELECT",
+ "test_package#zzz01:ADMIN -> test_package#zzz01: INSERT:test_domain",
+ "test_package#zzz01:ADMIN -> test_package#zzz01: INSERT:test_domain",
+ "test_package#zzz01:TENANT -> test_package#zzz01: SELECT",
+ "test_package#zzz02:ADMIN -> test_package#zzz02: INSERT:test_domain",
+ "test_package#zzz02:ADMIN -> test_package#zzz02: INSERT:test_domain",
+ "test_package#zzz02:TENANT -> test_package#zzz02: SELECT"
// @formatter:on
);
@@ -252,32 +252,32 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
- "test_customer#xxx.admin -> test_customer#xxx: SELECT",
- "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
+ "test_customer#xxx:ADMIN -> test_customer#xxx: INSERT:test_package",
+ "test_customer#xxx:ADMIN -> test_customer#xxx: SELECT",
+ "test_customer#xxx:TENANT -> test_customer#xxx: SELECT",
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
- "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:TENANT -> test_package#xxx00: SELECT",
+ "test_domain#xxx00-aaaa:OWNER -> test_domain#xxx00-aaaa: DELETE",
- "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
- "test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
- "test_package#xxx01.tenant -> test_package#xxx01: SELECT",
- "test_domain#xxx01-aaaa.owner -> test_domain#xxx01-aaaa: DELETE",
+ "test_package#xxx01:ADMIN -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01:ADMIN -> test_package#xxx01: INSERT:test_domain",
+ "test_package#xxx01:TENANT -> test_package#xxx01: SELECT",
+ "test_domain#xxx01-aaaa:OWNER -> test_domain#xxx01-aaaa: DELETE",
- "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
- "test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
- "test_package#xxx02.tenant -> test_package#xxx02: SELECT",
- "test_domain#xxx02-aaaa.owner -> test_domain#xxx02-aaaa: DELETE"
+ "test_package#xxx02:ADMIN -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02:ADMIN -> test_package#xxx02: INSERT:test_domain",
+ "test_package#xxx02:TENANT -> test_package#xxx02: SELECT",
+ "test_domain#xxx02-aaaa:OWNER -> test_domain#xxx02-aaaa: DELETE"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
- "test_customer#yyy.admin -> test_customer#yyy: SELECT",
- "test_customer#yyy.tenant -> test_customer#yyy: SELECT"
+ "test_customer#yyy:ADMIN -> test_customer#yyy: INSERT:test_package",
+ "test_customer#yyy:ADMIN -> test_customer#yyy: SELECT",
+ "test_customer#yyy:TENANT -> test_customer#yyy: SELECT"
// @formatter:on
);
}
@@ -312,26 +312,26 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
- // "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.tenant -> test_package#xxx00: SELECT",
- "test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
- "test_domain#xxx00-aaab.owner -> test_domain#xxx00-aaab: DELETE"
+ "test_customer#xxx:TENANT -> test_customer#xxx: SELECT",
+ // "test_customer#xxx:ADMIN -> test_customer#xxx: view" - Not permissions through the customer admin!
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:TENANT -> test_package#xxx00: SELECT",
+ "test_domain#xxx00-aaaa:OWNER -> test_domain#xxx00-aaaa: DELETE",
+ "test_domain#xxx00-aaab:OWNER -> test_domain#xxx00-aaab: DELETE"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
- "test_customer#yyy.admin -> test_customer#yyy: SELECT",
- "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
- "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
- "test_domain#yyy00-aaab.owner -> test_domain#yyy00-aaab: DELETE"
+ "test_customer#yyy:ADMIN -> test_customer#yyy: INSERT:test_package",
+ "test_customer#yyy:ADMIN -> test_customer#yyy: SELECT",
+ "test_customer#yyy:TENANT -> test_customer#yyy: SELECT",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:TENANT -> test_package#yyy00: SELECT",
+ "test_domain#yyy00-aaaa:OWNER -> test_domain#yyy00-aaaa: DELETE",
+ "test_domain#yyy00-aaab:OWNER -> test_domain#yyy00-aaab: DELETE"
// @formatter:on
);
}
@@ -360,26 +360,26 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
allTheseRbacPermissionsAreReturned(
result,
// @formatter:off
- "test_customer#xxx.tenant -> test_customer#xxx: SELECT",
- // "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
- "test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
- "test_package#xxx00.tenant -> test_package#xxx00: SELECT"
+ "test_customer#xxx:TENANT -> test_customer#xxx: SELECT",
+ // "test_customer#xxx:ADMIN -> test_customer#xxx: view" - Not permissions through the customer admin!
+ "test_package#xxx00:ADMIN -> test_package#xxx00: INSERT:test_domain",
+ "test_package#xxx00:TENANT -> test_package#xxx00: SELECT"
// @formatter:on
);
noneOfTheseRbacPermissionsAreReturned(
result,
// @formatter:off
// no customer admin permissions
- "test_customer#xxx.admin -> test_customer#xxx: add-package",
+ "test_customer#xxx:ADMIN -> test_customer#xxx: add-package",
// no permissions on other customer's objects
- "test_customer#yyy.admin -> test_customer#yyy: add-package",
- "test_customer#yyy.admin -> test_customer#yyy: SELECT",
- "test_customer#yyy.tenant -> test_customer#yyy: SELECT",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
- "test_package#yyy00.tenant -> test_package#yyy00: SELECT",
- "test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
- "test_domain#yyy00-xxxb.owner -> test_domain#yyy00-xxxb: DELETE"
+ "test_customer#yyy:ADMIN -> test_customer#yyy: add-package",
+ "test_customer#yyy:ADMIN -> test_customer#yyy: SELECT",
+ "test_customer#yyy:TENANT -> test_customer#yyy: SELECT",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:ADMIN -> test_package#yyy00: INSERT:test_domain",
+ "test_package#yyy00:TENANT -> test_package#yyy00: SELECT",
+ "test_domain#yyy00-aaaa:OWNER -> test_domain#yyy00-aaaa: DELETE",
+ "test_domain#yyy00-xxxb:OWNER -> test_domain#yyy00-xxxb: DELETE"
// @formatter:on
);
}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
index e9e1d47c..1d7bf4e5 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerControllerAcceptanceTest.java
@@ -89,7 +89,7 @@ class TestCustomerControllerAcceptanceTest {
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#yyy.admin")
+ .header("assumed-roles", "test_customer#yyy:ADMIN")
.port(port)
.when()
.get("http://localhost/api/test/customers")
@@ -148,7 +148,7 @@ class TestCustomerControllerAcceptanceTest {
// finally, the new customer can be viewed by its own admin
final var newUserUuid = UUID.fromString(
location.substring(location.lastIndexOf('/') + 1));
- context.define("superuser-fran@hostsharing.net", "test_customer#uuu.admin");
+ context.define("superuser-fran@hostsharing.net", "test_customer#uuu:ADMIN");
assertThat(testCustomerRepository.findByUuid(newUserUuid))
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("uuu"));
}
@@ -159,7 +159,7 @@ class TestCustomerControllerAcceptanceTest {
RestAssured // @formatter:off
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.contentType(ContentType.JSON)
.body("""
{
@@ -175,7 +175,7 @@ class TestCustomerControllerAcceptanceTest {
.statusCode(403)
.contentType(ContentType.JSON)
.statusCode(403)
- .body("message", containsString("insert into test_customer not allowed for current subjects {test_customer#xxx.admin}"));
+ .body("message", containsString("insert into test_customer not allowed for current subjects {test_customer#xxx:ADMIN}"));
// @formatter:on
// finally, the new customer was not created
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
index d576396a..962cef38 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntityUnitTest.java
@@ -21,9 +21,9 @@ class TestCustomerEntityUnitTest {
subgraph customer:roles[ ]
style customer:roles fill:#dd4901,stroke:white
- role:customer:owner[[customer:owner]]
- role:customer:admin[[customer:admin]]
- role:customer:tenant[[customer:tenant]]
+ role:customer:OWNER[[customer:OWNER]]
+ role:customer:ADMIN[[customer:ADMIN]]
+ role:customer:TENANT[[customer:TENANT]]
end
subgraph customer:permissions[ ]
@@ -37,18 +37,18 @@ class TestCustomerEntityUnitTest {
end
%% granting roles to users
- user:creator ==>|XX| role:customer:owner
+ user:creator ==>|XX| role:customer:OWNER
%% granting roles to roles
- role:global:admin ==>|XX| role:customer:owner
- role:customer:owner ==> role:customer:admin
- role:customer:admin ==> role:customer:tenant
+ role:global:ADMIN ==>|XX| role:customer:OWNER
+ role:customer:OWNER ==> role:customer:ADMIN
+ role:customer:ADMIN ==> role:customer:TENANT
%% granting permissions to roles
- role:global:admin ==> perm:customer:INSERT
- role:customer:owner ==> perm:customer:DELETE
- role:customer:admin ==> perm:customer:UPDATE
- role:customer:tenant ==> perm:customer:SELECT
+ role:global:ADMIN ==> perm:customer:INSERT
+ role:customer:OWNER ==> perm:customer:DELETE
+ role:customer:ADMIN ==> perm:customer:UPDATE
+ role:customer:TENANT ==> perm:customer:SELECT
""");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
index 27458b14..591ce0eb 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/cust/TestCustomerRepositoryIntegrationTest.java
@@ -54,7 +54,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void globalAdmin_withAssumedCustomerRole_cannotCreateNewCustomer() {
// given
- context("superuser-alex@hostsharing.net", "test_customer#xxx.admin");
+ context("superuser-alex@hostsharing.net", "test_customer#xxx:ADMIN");
// when
final var result = attempt(em, () -> {
@@ -66,7 +66,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
// then
result.assertExceptionWithRootCauseMessage(
PersistenceException.class,
- "ERROR: [403] insert into test_customer not allowed for current subjects {test_customer#xxx.admin}");
+ "ERROR: [403] insert into test_customer not allowed for current subjects {test_customer#xxx:ADMIN}");
}
@Test
@@ -112,7 +112,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void globalAdmin_withAssumedCustomerOwnerRole_canViewExactlyThatCustomer() {
given:
- context("superuser-alex@hostsharing.net", "test_customer#yyy.owner");
+ context("superuser-alex@hostsharing.net", "test_customer#yyy:OWNER");
// when
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
@@ -137,7 +137,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnCustomer() {
context("customer-admin@xxx.example.com");
- context("customer-admin@xxx.example.com", "test_package#xxx00.admin");
+ context("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java
index fd51ebf8..0e52cc40 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageControllerAcceptanceTest.java
@@ -44,7 +44,7 @@ class TestPackageControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.port(port)
.when()
.get("http://localhost/api/test/packages")
@@ -66,7 +66,7 @@ class TestPackageControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.port(port)
.when()
.get("http://localhost/api/test/packages?name=xxx01")
@@ -95,7 +95,7 @@ class TestPackageControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.contentType(ContentType.JSON)
.body(format("""
{
@@ -126,7 +126,7 @@ class TestPackageControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.contentType(ContentType.JSON)
.body("""
{
@@ -156,7 +156,7 @@ class TestPackageControllerAcceptanceTest {
RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.contentType(ContentType.JSON)
.body("{}")
.port(port)
@@ -176,7 +176,7 @@ class TestPackageControllerAcceptanceTest {
return UUID.fromString(RestAssured
.given()
.header("current-user", "superuser-alex@hostsharing.net")
- .header("assumed-roles", "test_customer#xxx.admin")
+ .header("assumed-roles", "test_customer#xxx:ADMIN")
.port(port)
.when()
.get("http://localhost/api/test/packages?name={packageName}", packageName)
@@ -188,7 +188,7 @@ class TestPackageControllerAcceptanceTest {
}
String getDescriptionOfPackage(final String packageName) {
- context.define("superuser-alex@hostsharing.net","test_customer#xxx.admin");
+ context.define("superuser-alex@hostsharing.net","test_customer#xxx:ADMIN");
return testPackageRepository.findAllByOptionalNameLike(packageName).get(0).getDescription();
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
index c5dccfd3..79dcfec2 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageEntityUnitTest.java
@@ -21,9 +21,9 @@ class TestPackageEntityUnitTest {
subgraph package:roles[ ]
style package:roles fill:#dd4901,stroke:white
- role:package:owner[[package:owner]]
- role:package:admin[[package:admin]]
- role:package:tenant[[package:tenant]]
+ role:package:OWNER[[package:OWNER]]
+ role:package:ADMIN[[package:ADMIN]]
+ role:package:TENANT[[package:TENANT]]
end
subgraph package:permissions[ ]
@@ -43,26 +43,26 @@ class TestPackageEntityUnitTest {
subgraph customer:roles[ ]
style customer:roles fill:#99bcdb,stroke:white
- role:customer:owner[[customer:owner]]
- role:customer:admin[[customer:admin]]
- role:customer:tenant[[customer:tenant]]
+ role:customer:OWNER[[customer:OWNER]]
+ role:customer:ADMIN[[customer:ADMIN]]
+ role:customer:TENANT[[customer:TENANT]]
end
end
%% granting roles to roles
- role:global:admin -.->|XX| role:customer:owner
- role:customer:owner -.-> role:customer:admin
- role:customer:admin -.-> role:customer:tenant
- role:customer:admin ==> role:package:owner
- role:package:owner ==> role:package:admin
- role:package:admin ==> role:package:tenant
- role:package:tenant ==> role:customer:tenant
+ role:global:ADMIN -.->|XX| role:customer:OWNER
+ role:customer:OWNER -.-> role:customer:ADMIN
+ role:customer:ADMIN -.-> role:customer:TENANT
+ role:customer:ADMIN ==> role:package:OWNER
+ role:package:OWNER ==> role:package:ADMIN
+ role:package:ADMIN ==> role:package:TENANT
+ role:package:TENANT ==> role:customer:TENANT
%% granting permissions to roles
- role:customer:admin ==> perm:package:INSERT
- role:package:owner ==> perm:package:DELETE
- role:package:owner ==> perm:package:UPDATE
- role:package:tenant ==> perm:package:SELECT
+ role:customer:ADMIN ==> perm:package:INSERT
+ role:package:OWNER ==> perm:package:DELETE
+ role:package:OWNER ==> perm:package:UPDATE
+ role:package:TENANT ==> perm:package:SELECT
""");
}
}
diff --git a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
index a201d79e..49412b3b 100644
--- a/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/test/pac/TestPackageRepositoryIntegrationTest.java
@@ -53,7 +53,7 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
given:
- context.define("superuser-alex@hostsharing.net", "global#global.admin");
+ context.define("superuser-alex@hostsharing.net", "global#global:ADMIN");
// when
final var result = testPackageRepository.findAllByOptionalNameLike(null);
@@ -76,7 +76,7 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnPackages() {
- context.define("customer-admin@xxx.example.com", "test_package#xxx00.admin");
+ context.define("customer-admin@xxx.example.com", "test_package#xxx00:ADMIN");
final var result = testPackageRepository.findAllByOptionalNameLike(null);
@@ -90,17 +90,17 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
@Test
public void supportsOptimisticLocking() {
// given
- globalAdminWithAssumedRole("test_package#xxx00.admin");
+ globalAdminWithAssumedRole("test_package#xxx00:ADMIN");
final var pac = testPackageRepository.findAllByOptionalNameLike("%").get(0);
// when
final var result1 = jpaAttempt.transacted(() -> {
- globalAdminWithAssumedRole("test_package#xxx00.owner");
+ globalAdminWithAssumedRole("test_package#xxx00:OWNER");
pac.setDescription("description set by thread 1");
testPackageRepository.save(pac);
});
final var result2 = jpaAttempt.transacted(() -> {
- globalAdminWithAssumedRole("test_package#xxx00.owner");
+ globalAdminWithAssumedRole("test_package#xxx00:OWNER");
pac.setDescription("description set by thread 2");
testPackageRepository.save(pac);
sleep(1500);
diff --git a/src/test/java/net/hostsharing/test/JpaAttempt.java b/src/test/java/net/hostsharing/test/JpaAttempt.java
index 3d5c50ee..86a332cd 100644
--- a/src/test/java/net/hostsharing/test/JpaAttempt.java
+++ b/src/test/java/net/hostsharing/test/JpaAttempt.java
@@ -154,6 +154,11 @@ public class JpaAttempt {
return this;
}
+ public JpaResult assertNotNull() {
+ assertThat(returnedValue()).isNotNull();
+ return this;
+ }
+
private String firstRootCauseMessageLineOf(final RuntimeException exception) {
final var rootCause = NestedExceptionUtils.getRootCause(exception);
return Optional.ofNullable(rootCause)
From 87af20a3a1bf4951a5e823011befe04c61039c10 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 12:29:31 +0200
Subject: [PATCH 09/12] structured-liquibase-files (#29)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/29
Reviewed-by: Timotheus Pokorra
---
.../HsOfficeBankAccountEntity.java | 2 +-
.../office/contact/HsOfficeContactEntity.java | 2 +-
.../HsOfficeCoopAssetsTransactionEntity.java | 3 +-
.../HsOfficeCoopSharesTransactionEntity.java | 3 +-
.../office/debitor/HsOfficeDebitorEntity.java | 2 +-
.../membership/HsOfficeMembershipEntity.java | 3 +-
.../partner/HsOfficePartnerDetailsEntity.java | 2 +-
.../office/partner/HsOfficePartnerEntity.java | 2 +-
.../office/person/HsOfficePersonEntity.java | 2 +-
.../relation/HsOfficeRelationEntity.java | 2 +-
.../HsOfficeSepaMandateEntity.java | 2 +-
.../test/cust/TestCustomerEntity.java | 2 +-
.../hsadminng/test/dom/TestDomainEntity.java | 2 +-
.../hsadminng/test/pac/TestPackageEntity.java | 2 +-
.../changelog/{ => 0-basis}/000-template.sql | 0
.../{ => 0-basis}/001-last-row-count.sql | 0
.../{ => 0-basis}/002-int-to-var.sql | 0
.../{ => 0-basis}/003-random-in-range.sql | 0
.../{ => 0-basis}/004-jsonb-changes-delta.sql | 0
.../{ => 0-basis}/005-uuid-ossp-extension.sql | 0
.../006-numeric-hash-functions.sql | 0
.../{ => 0-basis}/007-table-columns.sql | 0
.../{ => 0-basis}/009-check-environment.sql | 0
.../changelog/{ => 0-basis}/010-context.sql | 0
.../changelog/{ => 0-basis}/020-audit-log.sql | 0
.../1050-rbac-base.sql} | 0
.../1051-rbac-user-grant.sql} | 0
.../1054-rbac-context.sql} | 0
.../1055-rbac-views.sql} | 0
.../1056-rbac-trigger-context.sql} | 0
.../1057-rbac-role-builder.sql} | 0
.../1058-rbac-generators.sql} | 0
.../1059-rbac-statistics.sql} | 0
.../1080-rbac-global.sql} | 2 +-
.../201-test-customer/2010-test-customer.sql} | 0
.../2013-test-customer-rbac.md} | 0
.../2013-test-customer-rbac.sql} | 0
.../2018-test-customer-test-data.sql} | 0
.../202-test-package/2020-test-package.sql} | 0
.../2023-test-package-rbac.md} | 0
.../2023-test-package-rbac.sql} | 0
.../2028-test-package-test-data.sql} | 0
.../203-test-domain/2030-test-domain.sql} | 0
.../203-test-domain/2033-test-domain-rbac.md} | 0
.../2033-test-domain-rbac.sql} | 0
.../2038-test-domain-test-data.sql} | 0
.../501-contact/5010-hs-office-contact.sql} | 0
.../5013-hs-office-contact-rbac.md} | 0
.../5013-hs-office-contact-rbac.sql} | 0
.../5016-hs-office-contact-migration.sql} | 0
.../5018-hs-office-contact-test-data.sql} | 0
.../502-person/5020-hs-office-person.sql} | 0
.../502-person/5023-hs-office-person-rbac.md} | 0
.../5023-hs-office-person-rbac.sql} | 0
.../5028-hs-office-person-test-data.sql} | 0
.../503-relation/5030-hs-office-relation.sql} | 0
.../5033-hs-office-relation-rbac.md} | 0
.../5033-hs-office-relation-rbac.sql} | 0
.../5038-hs-office-relation-test-data.sql} | 0
.../504-partner/5040-hs-office-partner.sql} | 0
.../5043-hs-office-partner-rbac.md} | 0
.../5043-hs-office-partner-rbac.sql} | 0
.../5044-hs-office-partner-details-rbac.md} | 0
.../5044-hs-office-partner-details-rbac.sql} | 0
.../5046-hs-office-partner-migration.sql} | 0
.../5048-hs-office-partner-test-data.sql} | 0
.../5050-hs-office-bankaccount.sql} | 0
.../5053-hs-office-bankaccount-rbac.md} | 0
.../5053-hs-office-bankaccount-rbac.sql} | 0
.../5058-hs-office-bankaccount-test-data.sql} | 0
.../506-debitor/5060-hs-office-debitor.sql} | 0
.../5063-hs-office-debitor-rbac.md} | 0
.../5063-hs-office-debitor-rbac.sql} | 0
.../5068-hs-office-debitor-test-data.sql} | 0
.../5070-hs-office-sepamandate.sql} | 0
.../5073-hs-office-sepamandate-rbac.md} | 0
.../5073-hs-office-sepamandate-rbac.sql} | 0
.../5076-hs-office-sepamandate-migration.sql} | 0
.../5078-hs-office-sepamandate-test-data.sql} | 0
.../5100-hs-office-membership.sql} | 0
.../5103-hs-office-membership-rbac.md} | 0
.../5103-hs-office-membership-rbac.sql} | 0
.../5108-hs-office-membership-test-data.sql} | 0
.../5110-hs-office-coopshares.sql} | 0
.../5113-hs-office-coopshares-rbac.md} | 0
.../5113-hs-office-coopshares-rbac.sql} | 0
.../5116-hs-office-coopshares-migration.sql} | 0
.../5118-hs-office-coopshares-test-data.sql} | 0
.../5120-hs-office-coopassets.sql} | 0
.../5123-hs-office-coopassets-rbac.md} | 0
.../5123-hs-office-coopassets-rbac.sql} | 0
.../5126-hs-office-coopassets-migration.sql} | 0
.../5128-hs-office-coopassets-test-data.sql} | 0
.../db/changelog/db.changelog-master.yaml | 128 +++++++++---------
94 files changed, 82 insertions(+), 79 deletions(-)
rename src/main/resources/db/changelog/{ => 0-basis}/000-template.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/001-last-row-count.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/002-int-to-var.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/003-random-in-range.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/004-jsonb-changes-delta.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/005-uuid-ossp-extension.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/006-numeric-hash-functions.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/007-table-columns.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/009-check-environment.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/010-context.sql (100%)
rename src/main/resources/db/changelog/{ => 0-basis}/020-audit-log.sql (100%)
rename src/main/resources/db/changelog/{050-rbac-base.sql => 1-rbac/1050-rbac-base.sql} (100%)
rename src/main/resources/db/changelog/{051-rbac-user-grant.sql => 1-rbac/1051-rbac-user-grant.sql} (100%)
rename src/main/resources/db/changelog/{054-rbac-context.sql => 1-rbac/1054-rbac-context.sql} (100%)
rename src/main/resources/db/changelog/{055-rbac-views.sql => 1-rbac/1055-rbac-views.sql} (100%)
rename src/main/resources/db/changelog/{056-rbac-trigger-context.sql => 1-rbac/1056-rbac-trigger-context.sql} (100%)
rename src/main/resources/db/changelog/{057-rbac-role-builder.sql => 1-rbac/1057-rbac-role-builder.sql} (100%)
rename src/main/resources/db/changelog/{058-rbac-generators.sql => 1-rbac/1058-rbac-generators.sql} (100%)
rename src/main/resources/db/changelog/{059-rbac-statistics.sql => 1-rbac/1059-rbac-statistics.sql} (100%)
rename src/main/resources/db/changelog/{080-rbac-global.sql => 1-rbac/1080-rbac-global.sql} (98%)
rename src/main/resources/db/changelog/{110-test-customer.sql => 2-test/201-test-customer/2010-test-customer.sql} (100%)
rename src/main/resources/db/changelog/{113-test-customer-rbac.md => 2-test/201-test-customer/2013-test-customer-rbac.md} (100%)
rename src/main/resources/db/changelog/{113-test-customer-rbac.sql => 2-test/201-test-customer/2013-test-customer-rbac.sql} (100%)
rename src/main/resources/db/changelog/{118-test-customer-test-data.sql => 2-test/201-test-customer/2018-test-customer-test-data.sql} (100%)
rename src/main/resources/db/changelog/{120-test-package.sql => 2-test/202-test-package/2020-test-package.sql} (100%)
rename src/main/resources/db/changelog/{123-test-package-rbac.md => 2-test/202-test-package/2023-test-package-rbac.md} (100%)
rename src/main/resources/db/changelog/{123-test-package-rbac.sql => 2-test/202-test-package/2023-test-package-rbac.sql} (100%)
rename src/main/resources/db/changelog/{128-test-package-test-data.sql => 2-test/202-test-package/2028-test-package-test-data.sql} (100%)
rename src/main/resources/db/changelog/{130-test-domain.sql => 2-test/203-test-domain/2030-test-domain.sql} (100%)
rename src/main/resources/db/changelog/{133-test-domain-rbac.md => 2-test/203-test-domain/2033-test-domain-rbac.md} (100%)
rename src/main/resources/db/changelog/{133-test-domain-rbac.sql => 2-test/203-test-domain/2033-test-domain-rbac.sql} (100%)
rename src/main/resources/db/changelog/{138-test-domain-test-data.sql => 2-test/203-test-domain/2038-test-domain-test-data.sql} (100%)
rename src/main/resources/db/changelog/{200-hs-office-contact.sql => 5-hs-office/501-contact/5010-hs-office-contact.sql} (100%)
rename src/main/resources/db/changelog/{203-hs-office-contact-rbac.md => 5-hs-office/501-contact/5013-hs-office-contact-rbac.md} (100%)
rename src/main/resources/db/changelog/{203-hs-office-contact-rbac.sql => 5-hs-office/501-contact/5013-hs-office-contact-rbac.sql} (100%)
rename src/main/resources/db/changelog/{206-hs-office-contact-migration.sql => 5-hs-office/501-contact/5016-hs-office-contact-migration.sql} (100%)
rename src/main/resources/db/changelog/{208-hs-office-contact-test-data.sql => 5-hs-office/501-contact/5018-hs-office-contact-test-data.sql} (100%)
rename src/main/resources/db/changelog/{210-hs-office-person.sql => 5-hs-office/502-person/5020-hs-office-person.sql} (100%)
rename src/main/resources/db/changelog/{213-hs-office-person-rbac.md => 5-hs-office/502-person/5023-hs-office-person-rbac.md} (100%)
rename src/main/resources/db/changelog/{213-hs-office-person-rbac.sql => 5-hs-office/502-person/5023-hs-office-person-rbac.sql} (100%)
rename src/main/resources/db/changelog/{218-hs-office-person-test-data.sql => 5-hs-office/502-person/5028-hs-office-person-test-data.sql} (100%)
rename src/main/resources/db/changelog/{220-hs-office-relation.sql => 5-hs-office/503-relation/5030-hs-office-relation.sql} (100%)
rename src/main/resources/db/changelog/{223-hs-office-relation-rbac.md => 5-hs-office/503-relation/5033-hs-office-relation-rbac.md} (100%)
rename src/main/resources/db/changelog/{223-hs-office-relation-rbac.sql => 5-hs-office/503-relation/5033-hs-office-relation-rbac.sql} (100%)
rename src/main/resources/db/changelog/{228-hs-office-relation-test-data.sql => 5-hs-office/503-relation/5038-hs-office-relation-test-data.sql} (100%)
rename src/main/resources/db/changelog/{230-hs-office-partner.sql => 5-hs-office/504-partner/5040-hs-office-partner.sql} (100%)
rename src/main/resources/db/changelog/{233-hs-office-partner-rbac.md => 5-hs-office/504-partner/5043-hs-office-partner-rbac.md} (100%)
rename src/main/resources/db/changelog/{233-hs-office-partner-rbac.sql => 5-hs-office/504-partner/5043-hs-office-partner-rbac.sql} (100%)
rename src/main/resources/db/changelog/{234-hs-office-partner-details-rbac.md => 5-hs-office/504-partner/5044-hs-office-partner-details-rbac.md} (100%)
rename src/main/resources/db/changelog/{234-hs-office-partner-details-rbac.sql => 5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql} (100%)
rename src/main/resources/db/changelog/{236-hs-office-partner-migration.sql => 5-hs-office/504-partner/5046-hs-office-partner-migration.sql} (100%)
rename src/main/resources/db/changelog/{238-hs-office-partner-test-data.sql => 5-hs-office/504-partner/5048-hs-office-partner-test-data.sql} (100%)
rename src/main/resources/db/changelog/{240-hs-office-bankaccount.sql => 5-hs-office/505-bankaccount/5050-hs-office-bankaccount.sql} (100%)
rename src/main/resources/db/changelog/{243-hs-office-bankaccount-rbac.md => 5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.md} (100%)
rename src/main/resources/db/changelog/{243-hs-office-bankaccount-rbac.sql => 5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql} (100%)
rename src/main/resources/db/changelog/{248-hs-office-bankaccount-test-data.sql => 5-hs-office/505-bankaccount/5058-hs-office-bankaccount-test-data.sql} (100%)
rename src/main/resources/db/changelog/{270-hs-office-debitor.sql => 5-hs-office/506-debitor/5060-hs-office-debitor.sql} (100%)
rename src/main/resources/db/changelog/{273-hs-office-debitor-rbac.md => 5-hs-office/506-debitor/5063-hs-office-debitor-rbac.md} (100%)
rename src/main/resources/db/changelog/{273-hs-office-debitor-rbac.sql => 5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql} (100%)
rename src/main/resources/db/changelog/{278-hs-office-debitor-test-data.sql => 5-hs-office/506-debitor/5068-hs-office-debitor-test-data.sql} (100%)
rename src/main/resources/db/changelog/{250-hs-office-sepamandate.sql => 5-hs-office/507-sepamandate/5070-hs-office-sepamandate.sql} (100%)
rename src/main/resources/db/changelog/{253-hs-office-sepamandate-rbac.md => 5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.md} (100%)
rename src/main/resources/db/changelog/{253-hs-office-sepamandate-rbac.sql => 5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql} (100%)
rename src/main/resources/db/changelog/{256-hs-office-sepamandate-migration.sql => 5-hs-office/507-sepamandate/5076-hs-office-sepamandate-migration.sql} (100%)
rename src/main/resources/db/changelog/{258-hs-office-sepamandate-test-data.sql => 5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql} (100%)
rename src/main/resources/db/changelog/{300-hs-office-membership.sql => 5-hs-office/510-membership/5100-hs-office-membership.sql} (100%)
rename src/main/resources/db/changelog/{303-hs-office-membership-rbac.md => 5-hs-office/510-membership/5103-hs-office-membership-rbac.md} (100%)
rename src/main/resources/db/changelog/{303-hs-office-membership-rbac.sql => 5-hs-office/510-membership/5103-hs-office-membership-rbac.sql} (100%)
rename src/main/resources/db/changelog/{308-hs-office-membership-test-data.sql => 5-hs-office/510-membership/5108-hs-office-membership-test-data.sql} (100%)
rename src/main/resources/db/changelog/{310-hs-office-coopshares.sql => 5-hs-office/511-coopshares/5110-hs-office-coopshares.sql} (100%)
rename src/main/resources/db/changelog/{313-hs-office-coopshares-rbac.md => 5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.md} (100%)
rename src/main/resources/db/changelog/{313-hs-office-coopshares-rbac.sql => 5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql} (100%)
rename src/main/resources/db/changelog/{316-hs-office-coopshares-migration.sql => 5-hs-office/511-coopshares/5116-hs-office-coopshares-migration.sql} (100%)
rename src/main/resources/db/changelog/{318-hs-office-coopshares-test-data.sql => 5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql} (100%)
rename src/main/resources/db/changelog/{320-hs-office-coopassets.sql => 5-hs-office/512-coopassets/5120-hs-office-coopassets.sql} (100%)
rename src/main/resources/db/changelog/{323-hs-office-coopassets-rbac.md => 5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.md} (100%)
rename src/main/resources/db/changelog/{323-hs-office-coopassets-rbac.sql => 5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql} (100%)
rename src/main/resources/db/changelog/{326-hs-office-coopassets-migration.sql => 5-hs-office/512-coopassets/5126-hs-office-coopassets-migration.sql} (100%)
rename src/main/resources/db/changelog/{328-hs-office-coopassets-test-data.sql => 5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql} (100%)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
index 664ed8fe..99bb50ea 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
@@ -78,6 +78,6 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
index 62f5316a..4927b4bc 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
@@ -80,6 +80,6 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("203-hs-office-contact-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/501-contact/5013-hs-office-contact-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
index 03d3ae49..af2ea582 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
@@ -24,6 +24,7 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Optional;
@@ -128,6 +129,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("323-hs-office-coopassets-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
index 52222582..c62c1605 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
@@ -23,6 +23,7 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
+import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
@@ -123,6 +124,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("313-hs-office-coopshares-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index ee8e88a7..1c784078 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -188,6 +188,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("273-hs-office-debitor-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/506-debitor/5063-hs-office-debitor-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index f1f8ffff..71a8b1d0 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -28,6 +28,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -158,6 +159,6 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("303-hs-office-membership-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/510-membership/5103-hs-office-membership-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index 9a120ea3..a18dbc77 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -90,6 +90,6 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/504-partner/5044-hs-office-partner-details-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
index 41db9bfc..7c9346ea 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
@@ -113,6 +113,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("233-hs-office-partner-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/504-partner/5043-hs-office-partner-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
index b930f9b6..e8865ce5 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
@@ -86,6 +86,6 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("213-hs-office-person-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/502-person/5023-hs-office-person-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
index 5301983f..2077cf4a 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
@@ -130,6 +130,6 @@ public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("223-hs-office-relation-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/503-relation/5033-hs-office-relation-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index 897f89b8..403e2972 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -141,6 +141,6 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
+ rbac().generateWithBaseFileName("5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
index b4152fa9..94caa1de 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
@@ -57,6 +57,6 @@ public class TestCustomerEntity implements HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("113-test-customer-rbac");
+ rbac().generateWithBaseFileName("2-test/201-test-customer/2013-test-customer-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
index 70626f89..d3d387d7 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
@@ -67,6 +67,6 @@ public class TestDomainEntity implements HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("133-test-domain-rbac");
+ rbac().generateWithBaseFileName("2-test/203-test-domain/2033-test-domain-rbac");
}
}
diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
index 8f72fc4c..3ac28f34 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
@@ -68,6 +68,6 @@ public class TestPackageEntity implements HasUuid {
}
public static void main(String[] args) throws IOException {
- rbac().generateWithBaseFileName("123-test-package-rbac");
+ rbac().generateWithBaseFileName("2-test/202-test-package/2023-test-package-rbac");
}
}
diff --git a/src/main/resources/db/changelog/000-template.sql b/src/main/resources/db/changelog/0-basis/000-template.sql
similarity index 100%
rename from src/main/resources/db/changelog/000-template.sql
rename to src/main/resources/db/changelog/0-basis/000-template.sql
diff --git a/src/main/resources/db/changelog/001-last-row-count.sql b/src/main/resources/db/changelog/0-basis/001-last-row-count.sql
similarity index 100%
rename from src/main/resources/db/changelog/001-last-row-count.sql
rename to src/main/resources/db/changelog/0-basis/001-last-row-count.sql
diff --git a/src/main/resources/db/changelog/002-int-to-var.sql b/src/main/resources/db/changelog/0-basis/002-int-to-var.sql
similarity index 100%
rename from src/main/resources/db/changelog/002-int-to-var.sql
rename to src/main/resources/db/changelog/0-basis/002-int-to-var.sql
diff --git a/src/main/resources/db/changelog/003-random-in-range.sql b/src/main/resources/db/changelog/0-basis/003-random-in-range.sql
similarity index 100%
rename from src/main/resources/db/changelog/003-random-in-range.sql
rename to src/main/resources/db/changelog/0-basis/003-random-in-range.sql
diff --git a/src/main/resources/db/changelog/004-jsonb-changes-delta.sql b/src/main/resources/db/changelog/0-basis/004-jsonb-changes-delta.sql
similarity index 100%
rename from src/main/resources/db/changelog/004-jsonb-changes-delta.sql
rename to src/main/resources/db/changelog/0-basis/004-jsonb-changes-delta.sql
diff --git a/src/main/resources/db/changelog/005-uuid-ossp-extension.sql b/src/main/resources/db/changelog/0-basis/005-uuid-ossp-extension.sql
similarity index 100%
rename from src/main/resources/db/changelog/005-uuid-ossp-extension.sql
rename to src/main/resources/db/changelog/0-basis/005-uuid-ossp-extension.sql
diff --git a/src/main/resources/db/changelog/006-numeric-hash-functions.sql b/src/main/resources/db/changelog/0-basis/006-numeric-hash-functions.sql
similarity index 100%
rename from src/main/resources/db/changelog/006-numeric-hash-functions.sql
rename to src/main/resources/db/changelog/0-basis/006-numeric-hash-functions.sql
diff --git a/src/main/resources/db/changelog/007-table-columns.sql b/src/main/resources/db/changelog/0-basis/007-table-columns.sql
similarity index 100%
rename from src/main/resources/db/changelog/007-table-columns.sql
rename to src/main/resources/db/changelog/0-basis/007-table-columns.sql
diff --git a/src/main/resources/db/changelog/009-check-environment.sql b/src/main/resources/db/changelog/0-basis/009-check-environment.sql
similarity index 100%
rename from src/main/resources/db/changelog/009-check-environment.sql
rename to src/main/resources/db/changelog/0-basis/009-check-environment.sql
diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/0-basis/010-context.sql
similarity index 100%
rename from src/main/resources/db/changelog/010-context.sql
rename to src/main/resources/db/changelog/0-basis/010-context.sql
diff --git a/src/main/resources/db/changelog/020-audit-log.sql b/src/main/resources/db/changelog/0-basis/020-audit-log.sql
similarity index 100%
rename from src/main/resources/db/changelog/020-audit-log.sql
rename to src/main/resources/db/changelog/0-basis/020-audit-log.sql
diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/1-rbac/1050-rbac-base.sql
similarity index 100%
rename from src/main/resources/db/changelog/050-rbac-base.sql
rename to src/main/resources/db/changelog/1-rbac/1050-rbac-base.sql
diff --git a/src/main/resources/db/changelog/051-rbac-user-grant.sql b/src/main/resources/db/changelog/1-rbac/1051-rbac-user-grant.sql
similarity index 100%
rename from src/main/resources/db/changelog/051-rbac-user-grant.sql
rename to src/main/resources/db/changelog/1-rbac/1051-rbac-user-grant.sql
diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql
similarity index 100%
rename from src/main/resources/db/changelog/054-rbac-context.sql
rename to src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql
diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/1-rbac/1055-rbac-views.sql
similarity index 100%
rename from src/main/resources/db/changelog/055-rbac-views.sql
rename to src/main/resources/db/changelog/1-rbac/1055-rbac-views.sql
diff --git a/src/main/resources/db/changelog/056-rbac-trigger-context.sql b/src/main/resources/db/changelog/1-rbac/1056-rbac-trigger-context.sql
similarity index 100%
rename from src/main/resources/db/changelog/056-rbac-trigger-context.sql
rename to src/main/resources/db/changelog/1-rbac/1056-rbac-trigger-context.sql
diff --git a/src/main/resources/db/changelog/057-rbac-role-builder.sql b/src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql
similarity index 100%
rename from src/main/resources/db/changelog/057-rbac-role-builder.sql
rename to src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql
diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/1-rbac/1058-rbac-generators.sql
similarity index 100%
rename from src/main/resources/db/changelog/058-rbac-generators.sql
rename to src/main/resources/db/changelog/1-rbac/1058-rbac-generators.sql
diff --git a/src/main/resources/db/changelog/059-rbac-statistics.sql b/src/main/resources/db/changelog/1-rbac/1059-rbac-statistics.sql
similarity index 100%
rename from src/main/resources/db/changelog/059-rbac-statistics.sql
rename to src/main/resources/db/changelog/1-rbac/1059-rbac-statistics.sql
diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/1-rbac/1080-rbac-global.sql
similarity index 98%
rename from src/main/resources/db/changelog/080-rbac-global.sql
rename to src/main/resources/db/changelog/1-rbac/1080-rbac-global.sql
index 3078922f..c28a464d 100644
--- a/src/main/resources/db/changelog/080-rbac-global.sql
+++ b/src/main/resources/db/changelog/1-rbac/1080-rbac-global.sql
@@ -139,7 +139,7 @@ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'GU
$$;
begin transaction;
- call defineContext('creating role:global#globa:guest', null, null, null);
+ call defineContext('creating role:global#global:guest', null, null, null);
select createRole(globalGuest());
commit;
--//
diff --git a/src/main/resources/db/changelog/110-test-customer.sql b/src/main/resources/db/changelog/2-test/201-test-customer/2010-test-customer.sql
similarity index 100%
rename from src/main/resources/db/changelog/110-test-customer.sql
rename to src/main/resources/db/changelog/2-test/201-test-customer/2010-test-customer.sql
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.md b/src/main/resources/db/changelog/2-test/201-test-customer/2013-test-customer-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/113-test-customer-rbac.md
rename to src/main/resources/db/changelog/2-test/201-test-customer/2013-test-customer-rbac.md
diff --git a/src/main/resources/db/changelog/113-test-customer-rbac.sql b/src/main/resources/db/changelog/2-test/201-test-customer/2013-test-customer-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/113-test-customer-rbac.sql
rename to src/main/resources/db/changelog/2-test/201-test-customer/2013-test-customer-rbac.sql
diff --git a/src/main/resources/db/changelog/118-test-customer-test-data.sql b/src/main/resources/db/changelog/2-test/201-test-customer/2018-test-customer-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/118-test-customer-test-data.sql
rename to src/main/resources/db/changelog/2-test/201-test-customer/2018-test-customer-test-data.sql
diff --git a/src/main/resources/db/changelog/120-test-package.sql b/src/main/resources/db/changelog/2-test/202-test-package/2020-test-package.sql
similarity index 100%
rename from src/main/resources/db/changelog/120-test-package.sql
rename to src/main/resources/db/changelog/2-test/202-test-package/2020-test-package.sql
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.md b/src/main/resources/db/changelog/2-test/202-test-package/2023-test-package-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/123-test-package-rbac.md
rename to src/main/resources/db/changelog/2-test/202-test-package/2023-test-package-rbac.md
diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/2-test/202-test-package/2023-test-package-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/123-test-package-rbac.sql
rename to src/main/resources/db/changelog/2-test/202-test-package/2023-test-package-rbac.sql
diff --git a/src/main/resources/db/changelog/128-test-package-test-data.sql b/src/main/resources/db/changelog/2-test/202-test-package/2028-test-package-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/128-test-package-test-data.sql
rename to src/main/resources/db/changelog/2-test/202-test-package/2028-test-package-test-data.sql
diff --git a/src/main/resources/db/changelog/130-test-domain.sql b/src/main/resources/db/changelog/2-test/203-test-domain/2030-test-domain.sql
similarity index 100%
rename from src/main/resources/db/changelog/130-test-domain.sql
rename to src/main/resources/db/changelog/2-test/203-test-domain/2030-test-domain.sql
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.md b/src/main/resources/db/changelog/2-test/203-test-domain/2033-test-domain-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/133-test-domain-rbac.md
rename to src/main/resources/db/changelog/2-test/203-test-domain/2033-test-domain-rbac.md
diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/2-test/203-test-domain/2033-test-domain-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/133-test-domain-rbac.sql
rename to src/main/resources/db/changelog/2-test/203-test-domain/2033-test-domain-rbac.sql
diff --git a/src/main/resources/db/changelog/138-test-domain-test-data.sql b/src/main/resources/db/changelog/2-test/203-test-domain/2038-test-domain-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/138-test-domain-test-data.sql
rename to src/main/resources/db/changelog/2-test/203-test-domain/2038-test-domain-test-data.sql
diff --git a/src/main/resources/db/changelog/200-hs-office-contact.sql b/src/main/resources/db/changelog/5-hs-office/501-contact/5010-hs-office-contact.sql
similarity index 100%
rename from src/main/resources/db/changelog/200-hs-office-contact.sql
rename to src/main/resources/db/changelog/5-hs-office/501-contact/5010-hs-office-contact.sql
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.md b/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/203-hs-office-contact-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.md
diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/203-hs-office-contact-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql
diff --git a/src/main/resources/db/changelog/206-hs-office-contact-migration.sql b/src/main/resources/db/changelog/5-hs-office/501-contact/5016-hs-office-contact-migration.sql
similarity index 100%
rename from src/main/resources/db/changelog/206-hs-office-contact-migration.sql
rename to src/main/resources/db/changelog/5-hs-office/501-contact/5016-hs-office-contact-migration.sql
diff --git a/src/main/resources/db/changelog/208-hs-office-contact-test-data.sql b/src/main/resources/db/changelog/5-hs-office/501-contact/5018-hs-office-contact-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/208-hs-office-contact-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/501-contact/5018-hs-office-contact-test-data.sql
diff --git a/src/main/resources/db/changelog/210-hs-office-person.sql b/src/main/resources/db/changelog/5-hs-office/502-person/5020-hs-office-person.sql
similarity index 100%
rename from src/main/resources/db/changelog/210-hs-office-person.sql
rename to src/main/resources/db/changelog/5-hs-office/502-person/5020-hs-office-person.sql
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.md b/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/213-hs-office-person-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.md
diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/213-hs-office-person-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql
diff --git a/src/main/resources/db/changelog/218-hs-office-person-test-data.sql b/src/main/resources/db/changelog/5-hs-office/502-person/5028-hs-office-person-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/218-hs-office-person-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/502-person/5028-hs-office-person-test-data.sql
diff --git a/src/main/resources/db/changelog/220-hs-office-relation.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql
similarity index 100%
rename from src/main/resources/db/changelog/220-hs-office-relation.sql
rename to src/main/resources/db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.md b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/223-hs-office-relation-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.md
diff --git a/src/main/resources/db/changelog/223-hs-office-relation-rbac.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/223-hs-office-relation-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql
diff --git a/src/main/resources/db/changelog/228-hs-office-relation-test-data.sql b/src/main/resources/db/changelog/5-hs-office/503-relation/5038-hs-office-relation-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/228-hs-office-relation-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/503-relation/5038-hs-office-relation-test-data.sql
diff --git a/src/main/resources/db/changelog/230-hs-office-partner.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5040-hs-office-partner.sql
similarity index 100%
rename from src/main/resources/db/changelog/230-hs-office-partner.sql
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5040-hs-office-partner.sql
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.md b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/233-hs-office-partner-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.md
diff --git a/src/main/resources/db/changelog/233-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/233-hs-office-partner-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md b/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/234-hs-office-partner-details-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.md
diff --git a/src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/234-hs-office-partner-details-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql
diff --git a/src/main/resources/db/changelog/236-hs-office-partner-migration.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5046-hs-office-partner-migration.sql
similarity index 100%
rename from src/main/resources/db/changelog/236-hs-office-partner-migration.sql
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5046-hs-office-partner-migration.sql
diff --git a/src/main/resources/db/changelog/238-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/5-hs-office/504-partner/5048-hs-office-partner-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/238-hs-office-partner-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/504-partner/5048-hs-office-partner-test-data.sql
diff --git a/src/main/resources/db/changelog/240-hs-office-bankaccount.sql b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5050-hs-office-bankaccount.sql
similarity index 100%
rename from src/main/resources/db/changelog/240-hs-office-bankaccount.sql
rename to src/main/resources/db/changelog/5-hs-office/505-bankaccount/5050-hs-office-bankaccount.sql
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.md
diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql
diff --git a/src/main/resources/db/changelog/248-hs-office-bankaccount-test-data.sql b/src/main/resources/db/changelog/5-hs-office/505-bankaccount/5058-hs-office-bankaccount-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/248-hs-office-bankaccount-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/505-bankaccount/5058-hs-office-bankaccount-test-data.sql
diff --git a/src/main/resources/db/changelog/270-hs-office-debitor.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
similarity index 100%
rename from src/main/resources/db/changelog/270-hs-office-debitor.sql
rename to src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.md b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/273-hs-office-debitor-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.md
diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5068-hs-office-debitor-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/506-debitor/5068-hs-office-debitor-test-data.sql
diff --git a/src/main/resources/db/changelog/250-hs-office-sepamandate.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5070-hs-office-sepamandate.sql
similarity index 100%
rename from src/main/resources/db/changelog/250-hs-office-sepamandate.sql
rename to src/main/resources/db/changelog/5-hs-office/507-sepamandate/5070-hs-office-sepamandate.sql
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.md
diff --git a/src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/253-hs-office-sepamandate-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql
diff --git a/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5076-hs-office-sepamandate-migration.sql
similarity index 100%
rename from src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql
rename to src/main/resources/db/changelog/5-hs-office/507-sepamandate/5076-hs-office-sepamandate-migration.sql
diff --git a/src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/258-hs-office-sepamandate-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
similarity index 100%
rename from src/main/resources/db/changelog/300-hs-office-membership.sql
rename to src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.md b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/303-hs-office-membership-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.md
diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/303-hs-office-membership-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql
diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5108-hs-office-membership-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/308-hs-office-membership-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/510-membership/5108-hs-office-membership-test-data.sql
diff --git a/src/main/resources/db/changelog/310-hs-office-coopshares.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql
similarity index 100%
rename from src/main/resources/db/changelog/310-hs-office-coopshares.sql
rename to src/main/resources/db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/313-hs-office-coopshares-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.md
diff --git a/src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/313-hs-office-coopshares-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql
diff --git a/src/main/resources/db/changelog/316-hs-office-coopshares-migration.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5116-hs-office-coopshares-migration.sql
similarity index 100%
rename from src/main/resources/db/changelog/316-hs-office-coopshares-migration.sql
rename to src/main/resources/db/changelog/5-hs-office/511-coopshares/5116-hs-office-coopshares-migration.sql
diff --git a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql b/src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql
diff --git a/src/main/resources/db/changelog/320-hs-office-coopassets.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql
similarity index 100%
rename from src/main/resources/db/changelog/320-hs-office-coopassets.sql
rename to src/main/resources/db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.md
similarity index 100%
rename from src/main/resources/db/changelog/323-hs-office-coopassets-rbac.md
rename to src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.md
diff --git a/src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql
similarity index 100%
rename from src/main/resources/db/changelog/323-hs-office-coopassets-rbac.sql
rename to src/main/resources/db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql
diff --git a/src/main/resources/db/changelog/326-hs-office-coopassets-migration.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5126-hs-office-coopassets-migration.sql
similarity index 100%
rename from src/main/resources/db/changelog/326-hs-office-coopassets-migration.sql
rename to src/main/resources/db/changelog/5-hs-office/512-coopassets/5126-hs-office-coopassets-migration.sql
diff --git a/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql b/src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql
similarity index 100%
rename from src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql
rename to src/main/resources/db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 6047befa..11a5f956 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -1,129 +1,129 @@
databaseChangeLog:
- include:
- file: db/changelog/001-last-row-count.sql
+ file: db/changelog/0-basis/001-last-row-count.sql
- include:
- file: db/changelog/002-int-to-var.sql
+ file: db/changelog/0-basis/002-int-to-var.sql
- include:
- file: db/changelog/003-random-in-range.sql
+ file: db/changelog/0-basis/003-random-in-range.sql
- include:
- file: db/changelog/004-jsonb-changes-delta.sql
+ file: db/changelog/0-basis/004-jsonb-changes-delta.sql
- include:
- file: db/changelog/005-uuid-ossp-extension.sql
+ file: db/changelog/0-basis/005-uuid-ossp-extension.sql
- include:
- file: db/changelog/006-numeric-hash-functions.sql
+ file: db/changelog/0-basis/006-numeric-hash-functions.sql
- include:
- file: db/changelog/007-table-columns.sql
+ file: db/changelog/0-basis/007-table-columns.sql
- include:
- file: db/changelog/009-check-environment.sql
+ file: db/changelog/0-basis/009-check-environment.sql
- include:
- file: db/changelog/010-context.sql
+ file: db/changelog/0-basis/010-context.sql
- include:
- file: db/changelog/020-audit-log.sql
+ file: db/changelog/0-basis/020-audit-log.sql
- include:
- file: db/changelog/050-rbac-base.sql
+ file: db/changelog/1-rbac/1050-rbac-base.sql
- include:
- file: db/changelog/051-rbac-user-grant.sql
+ file: db/changelog/1-rbac/1051-rbac-user-grant.sql
- include:
- file: db/changelog/054-rbac-context.sql
+ file: db/changelog/1-rbac/1054-rbac-context.sql
- include:
- file: db/changelog/055-rbac-views.sql
+ file: db/changelog/1-rbac/1055-rbac-views.sql
- include:
- file: db/changelog/056-rbac-trigger-context.sql
+ file: db/changelog/1-rbac/1056-rbac-trigger-context.sql
- include:
- file: db/changelog/057-rbac-role-builder.sql
+ file: db/changelog/1-rbac/1057-rbac-role-builder.sql
- include:
- file: db/changelog/058-rbac-generators.sql
+ file: db/changelog/1-rbac/1058-rbac-generators.sql
- include:
- file: db/changelog/059-rbac-statistics.sql
+ file: db/changelog/1-rbac/1059-rbac-statistics.sql
- include:
- file: db/changelog/080-rbac-global.sql
+ file: db/changelog/1-rbac/1080-rbac-global.sql
- include:
- file: db/changelog/110-test-customer.sql
+ file: db/changelog/2-test/201-test-customer/2010-test-customer.sql
- include:
- file: db/changelog/113-test-customer-rbac.sql
+ file: db/changelog/2-test/201-test-customer/2013-test-customer-rbac.sql
- include:
- file: db/changelog/118-test-customer-test-data.sql
+ file: db/changelog/2-test/201-test-customer/2018-test-customer-test-data.sql
- include:
- file: db/changelog/120-test-package.sql
+ file: db/changelog/2-test/202-test-package/2020-test-package.sql
- include:
- file: db/changelog/123-test-package-rbac.sql
+ file: db/changelog/2-test/202-test-package/2023-test-package-rbac.sql
- include:
- file: db/changelog/128-test-package-test-data.sql
+ file: db/changelog/2-test/202-test-package/2028-test-package-test-data.sql
- include:
- file: db/changelog/130-test-domain.sql
+ file: db/changelog/2-test/203-test-domain/2030-test-domain.sql
- include:
- file: db/changelog/133-test-domain-rbac.sql
+ file: db/changelog/2-test/203-test-domain/2033-test-domain-rbac.sql
- include:
- file: db/changelog/138-test-domain-test-data.sql
+ file: db/changelog/2-test/203-test-domain/2038-test-domain-test-data.sql
- include:
- file: db/changelog/200-hs-office-contact.sql
+ file: db/changelog/5-hs-office/501-contact/5010-hs-office-contact.sql
- include:
- file: db/changelog/203-hs-office-contact-rbac.sql
+ file: db/changelog/5-hs-office/501-contact/5013-hs-office-contact-rbac.sql
- include:
- file: db/changelog/206-hs-office-contact-migration.sql
+ file: db/changelog/5-hs-office/501-contact/5016-hs-office-contact-migration.sql
- include:
- file: db/changelog/208-hs-office-contact-test-data.sql
+ file: db/changelog/5-hs-office/501-contact/5018-hs-office-contact-test-data.sql
- include:
- file: db/changelog/210-hs-office-person.sql
+ file: db/changelog/5-hs-office/502-person/5020-hs-office-person.sql
- include:
- file: db/changelog/213-hs-office-person-rbac.sql
+ file: db/changelog/5-hs-office/502-person/5023-hs-office-person-rbac.sql
- include:
- file: db/changelog/218-hs-office-person-test-data.sql
+ file: db/changelog/5-hs-office/502-person/5028-hs-office-person-test-data.sql
- include:
- file: db/changelog/220-hs-office-relation.sql
+ file: db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql
- include:
- file: db/changelog/223-hs-office-relation-rbac.sql
+ file: db/changelog/5-hs-office/503-relation/5033-hs-office-relation-rbac.sql
- include:
- file: db/changelog/228-hs-office-relation-test-data.sql
+ file: db/changelog/5-hs-office/503-relation/5038-hs-office-relation-test-data.sql
- include:
- file: db/changelog/230-hs-office-partner.sql
+ file: db/changelog/5-hs-office/504-partner/5040-hs-office-partner.sql
- include:
- file: db/changelog/233-hs-office-partner-rbac.sql
+ file: db/changelog/5-hs-office/504-partner/5043-hs-office-partner-rbac.sql
- include:
- file: db/changelog/234-hs-office-partner-details-rbac.sql
+ file: db/changelog/5-hs-office/504-partner/5044-hs-office-partner-details-rbac.sql
- include:
- file: db/changelog/236-hs-office-partner-migration.sql
+ file: db/changelog/5-hs-office/504-partner/5046-hs-office-partner-migration.sql
- include:
- file: db/changelog/238-hs-office-partner-test-data.sql
+ file: db/changelog/5-hs-office/504-partner/5048-hs-office-partner-test-data.sql
- include:
- file: db/changelog/240-hs-office-bankaccount.sql
+ file: db/changelog/5-hs-office/505-bankaccount/5050-hs-office-bankaccount.sql
- include:
- file: db/changelog/243-hs-office-bankaccount-rbac.sql
+ file: db/changelog/5-hs-office/505-bankaccount/5053-hs-office-bankaccount-rbac.sql
- include:
- file: db/changelog/248-hs-office-bankaccount-test-data.sql
+ file: db/changelog/5-hs-office/505-bankaccount/5058-hs-office-bankaccount-test-data.sql
- include:
- file: db/changelog/270-hs-office-debitor.sql
+ file: db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
- include:
- file: db/changelog/273-hs-office-debitor-rbac.sql
+ file: db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
- include:
- file: db/changelog/278-hs-office-debitor-test-data.sql
+ file: db/changelog/5-hs-office/506-debitor/5068-hs-office-debitor-test-data.sql
- include:
- file: db/changelog/250-hs-office-sepamandate.sql
+ file: db/changelog/5-hs-office/507-sepamandate/5070-hs-office-sepamandate.sql
- include:
- file: db/changelog/253-hs-office-sepamandate-rbac.sql
+ file: db/changelog/5-hs-office/507-sepamandate/5073-hs-office-sepamandate-rbac.sql
- include:
- file: db/changelog/256-hs-office-sepamandate-migration.sql
+ file: db/changelog/5-hs-office/507-sepamandate/5076-hs-office-sepamandate-migration.sql
- include:
- file: db/changelog/258-hs-office-sepamandate-test-data.sql
+ file: db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
- include:
- file: db/changelog/300-hs-office-membership.sql
+ file: db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
- include:
- file: db/changelog/303-hs-office-membership-rbac.sql
+ file: db/changelog/5-hs-office/510-membership/5103-hs-office-membership-rbac.sql
- include:
- file: db/changelog/308-hs-office-membership-test-data.sql
+ file: db/changelog/5-hs-office/510-membership/5108-hs-office-membership-test-data.sql
- include:
- file: db/changelog/310-hs-office-coopshares.sql
+ file: db/changelog/5-hs-office/511-coopshares/5110-hs-office-coopshares.sql
- include:
- file: db/changelog/313-hs-office-coopshares-rbac.sql
+ file: db/changelog/5-hs-office/511-coopshares/5113-hs-office-coopshares-rbac.sql
- include:
- file: db/changelog/316-hs-office-coopshares-migration.sql
+ file: db/changelog/5-hs-office/511-coopshares/5116-hs-office-coopshares-migration.sql
- include:
- file: db/changelog/318-hs-office-coopshares-test-data.sql
+ file: db/changelog/5-hs-office/511-coopshares/5118-hs-office-coopshares-test-data.sql
- include:
- file: db/changelog/320-hs-office-coopassets.sql
+ file: db/changelog/5-hs-office/512-coopassets/5120-hs-office-coopassets.sql
- include:
- file: db/changelog/323-hs-office-coopassets-rbac.sql
+ file: db/changelog/5-hs-office/512-coopassets/5123-hs-office-coopassets-rbac.sql
- include:
- file: db/changelog/326-hs-office-coopassets-migration.sql
+ file: db/changelog/5-hs-office/512-coopassets/5126-hs-office-coopassets-migration.sql
- include:
- file: db/changelog/328-hs-office-coopassets-test-data.sql
+ file: db/changelog/5-hs-office/512-coopassets/5128-hs-office-coopassets-test-data.sql
From 277369a960d7e51ba5b542278ae9e3bbab1a4c35 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 13:09:12 +0200
Subject: [PATCH 10/12] debitornumbersuffix-as-string (#30)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/30
Reviewed-by: Timotheus Pokorra
---
.../hs/office/debitor/HsOfficeDebitorEntity.java | 11 +++++++----
.../office/membership/HsOfficeMembershipEntity.java | 3 +++
.../506-debitor/5060-hs-office-debitor.sql | 2 +-
.../506-debitor/5063-hs-office-debitor-rbac.sql | 2 +-
.../5078-hs-office-sepamandate-test-data.sql | 8 ++++----
.../510-membership/5100-hs-office-membership.sql | 3 +--
.../HsOfficeDebitorControllerAcceptanceTest.java | 6 +++++-
.../office/debitor/HsOfficeDebitorEntityUnitTest.java | 10 +++++-----
.../HsOfficeDebitorRepositoryIntegrationTest.java | 8 ++++----
.../hs/office/debitor/TestHsOfficeDebitor.java | 2 +-
.../hs/office/migration/ImportOfficeData.java | 2 +-
11 files changed, 33 insertions(+), 24 deletions(-)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 1c784078..0a63d0b1 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -16,6 +16,7 @@ import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*;
+import jakarta.validation.constraints.Pattern;
import java.io.IOException;
import java.util.UUID;
@@ -45,6 +46,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
+ public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
private static Stringify stringify =
stringify(HsOfficeDebitorEntity.class, "debitor")
@@ -75,8 +77,9 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerEntity partner;
- @Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
- private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
+ @Column(name = "debitornumbersuffix", length = 2)
+ @Pattern(regexp = TWO_DECIMAL_DIGITS)
+ private String debitorNumberSuffix;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
@JoinColumn(name = "debitorreluuid", nullable = false)
@@ -109,7 +112,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
.filter(partner -> debitorNumberSuffix != null)
.map(HsOfficePartnerEntity::getPartnerNumber)
.map(Object::toString)
- .map(partnerNumber -> partnerNumber + String.format("%02d", debitorNumberSuffix))
+ .map(partnerNumber -> partnerNumber + debitorNumberSuffix)
.orElse(null);
}
@@ -138,7 +141,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
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
+ || debitorNumberSuffix as idName
FROM hs_office_debitor AS debitor
"""))
.withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index 71a8b1d0..801d9033 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -14,6 +14,7 @@ import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;
import jakarta.persistence.*;
+import jakarta.validation.constraints.Pattern;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
@@ -44,6 +45,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
public static final String MEMBER_NUMBER_TAG = "M-";
+ public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
private static Stringify stringify = stringify(HsOfficeMembershipEntity.class)
.withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
@@ -61,6 +63,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
private HsOfficePartnerEntity partner;
@Column(name = "membernumbersuffix", length = 2)
+ @Pattern(regexp = TWO_DECIMAL_DIGITS)
private String memberNumberSuffix;
@Column(name = "validity", columnDefinition = "daterange")
diff --git a/src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
index e2174eca..59ad01e0 100644
--- a/src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
+++ b/src/main/resources/db/changelog/5-hs-office/506-debitor/5060-hs-office-debitor.sql
@@ -7,7 +7,7 @@
create table hs_office_debitor
(
uuid uuid unique references RbacObject (uuid) initially deferred,
- debitorNumberSuffix numeric(2) not null,
+ debitorNumberSuffix char(2) not null check (debitorNumberSuffix::text ~ '^[0-9][0-9]$'),
debitorRelUuid uuid not null references hs_office_relation(uuid),
billable boolean not null default true,
vatId varchar(24), -- TODO.spec: here or in person?
diff --git a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
index 152f980e..59ac43e8 100644
--- a/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
+++ b/src/main/resources/db/changelog/5-hs-office/506-debitor/5063-hs-office-debitor-rbac.sql
@@ -201,7 +201,7 @@ create trigger hs_office_debitor_insert_permission_check_tg
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
+ || debitorNumberSuffix as idName
FROM hs_office_debitor AS debitor
$idName$);
--//
diff --git a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
index 69d39165..e664d8c5 100644
--- a/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
+++ b/src/main/resources/db/changelog/5-hs-office/507-sepamandate/5078-hs-office-sepamandate-test-data.sql
@@ -10,7 +10,7 @@
*/
create or replace procedure createHsOfficeSepaMandateTestData(
forPartnerNumber numeric(5),
- forDebitorSuffix numeric(2),
+ forDebitorSuffix char(2),
forIban varchar,
withReference varchar)
language plpgsql as $$
@@ -48,9 +48,9 @@ end; $$;
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');
+ 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;
$$;
--//
diff --git a/src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql b/src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
index f2a560e2..28ec1249 100644
--- a/src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
+++ b/src/main/resources/db/changelog/5-hs-office/510-membership/5100-hs-office-membership.sql
@@ -12,8 +12,7 @@ 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]$'),
+ 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,
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
index c2e3fffd..07ecb5f5 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java
@@ -722,7 +722,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0);
final var newDebitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix(++nextDebitorSuffix)
+ .debitorNumberSuffix(nextDebitorSuffix())
.billable(true)
.debitorRel(
HsOfficeRelationEntity.builder()
@@ -751,4 +751,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
System.out.printf("deleted %d entities%n", count);
});
}
+
+ private String nextDebitorSuffix() {
+ return String.format("%02d", nextDebitorSuffix++);
+ }
}
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
index 3ad1c8ea..cb629b2b 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java
@@ -26,7 +26,7 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)67)
+ .debitorNumberSuffix("67")
.debitorRel(givenDebitorRel)
.defaultPrefix("som")
.partner(HsOfficePartnerEntity.builder()
@@ -43,7 +43,7 @@ class HsOfficeDebitorEntityUnitTest {
void toShortStringContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel)
- .debitorNumberSuffix((byte)67)
+ .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
@@ -58,7 +58,7 @@ class HsOfficeDebitorEntityUnitTest {
void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() {
final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel)
- .debitorNumberSuffix((byte)67)
+ .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
@@ -73,7 +73,7 @@ class HsOfficeDebitorEntityUnitTest {
void getDebitorNumberWithoutPartnerReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel)
- .debitorNumberSuffix((byte)67)
+ .debitorNumberSuffix("67")
.partner(null)
.build();
@@ -86,7 +86,7 @@ class HsOfficeDebitorEntityUnitTest {
void getDebitorNumberWithoutPartnerNumberReturnsNull() {
final var given = HsOfficeDebitorEntity.builder()
.debitorRel(givenDebitorRel)
- .debitorNumberSuffix((byte)67)
+ .debitorNumberSuffix("67")
.partner(HsOfficePartnerEntity.builder().build())
.build();
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
index 7a3dfbb7..32f441af 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java
@@ -89,7 +89,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)21)
+ .debitorNumberSuffix("21")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
.anchor(givenPartnerPerson)
@@ -121,7 +121,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)21)
+ .debitorNumberSuffix("21")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
.anchor(givenPartnerPerson)
@@ -156,7 +156,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitorPerson = one(personRepo.findPersonByOptionalNameLike("Fourth eG"));
final var givenContact = one(contactRepo.findContactByOptionalLabelLike("fourth contact"));
final var newDebitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)22)
+ .debitorNumberSuffix("22")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
.anchor(givenPartnerPerson)
@@ -613,7 +613,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenBankAccount =
bankAccountHolder != null ? one(bankAccountRepo.findByOptionalHolderLike(bankAccountHolder)) : null;
final var newDebitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte)20)
+ .debitorNumberSuffix("20")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
.anchor(givenPartnerPerson)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
index 2970ea1b..4305b87a 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java
@@ -10,7 +10,7 @@ import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TE
@UtilityClass
public class TestHsOfficeDebitor {
- public byte DEFAULT_DEBITOR_SUFFIX = 0;
+ public String DEFAULT_DEBITOR_SUFFIX = "00";
public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
index 4010167d..8da1f12f 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
@@ -724,7 +724,7 @@ public class ImportOfficeData extends ContextBasedTest {
relations.put(relationId++, debitorRel);
final var debitor = HsOfficeDebitorEntity.builder()
- .debitorNumberSuffix((byte) 0)
+ .debitorNumberSuffix("00")
.partner(partner)
.debitorRel(debitorRel)
.defaultPrefix(rec.getString("member_code").replace("hsh00-", ""))
From ad04faa21de837b8e80c8e77d9da7bcf4317d319 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 13:14:46 +0200
Subject: [PATCH 11/12] cleanup-todos (#31)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/31
Reviewed-by: Timotheus Pokorra
---
.gitignore | 1 -
README.md | 9 +----
doc/test-concept.md | 4 +-
sql/historization.sql | 32 +++++++--------
.../hsadminng/context/Context.java | 19 +++++----
.../HsOfficeBankAccountEntity.java | 4 +-
.../office/contact/HsOfficeContactEntity.java | 10 ++---
.../HsOfficeCoopAssetsTransactionEntity.java | 6 ++-
.../HsOfficeCoopSharesTransactionEntity.java | 7 ++--
.../office/debitor/HsOfficeDebitorEntity.java | 6 +--
.../membership/HsOfficeMembershipEntity.java | 5 ++-
.../partner/HsOfficePartnerDetailsEntity.java | 4 +-
.../office/partner/HsOfficePartnerEntity.java | 4 +-
.../partner/HsOfficePartnerRepository.java | 2 +-
.../office/person/HsOfficePersonEntity.java | 4 +-
.../relation/HsOfficeRelationEntity.java | 4 +-
.../HsOfficeSepaMandateEntity.java | 4 +-
.../hsadminng/persistence/HasUuid.java | 7 ----
.../hsadminng/rbac/rbacdef/RbacView.java | 14 +++----
.../test/cust/TestCustomerEntity.java | 4 +-
.../hsadminng/test/dom/TestDomainEntity.java | 4 +-
.../hsadminng/test/pac/TestPackageEntity.java | 4 +-
.../rbac/rbac-role-schemas.yaml | 2 +-
.../db/changelog/0-basis/010-context.sql | 8 ++--
.../db/changelog/1-rbac/1054-rbac-context.sql | 8 ++--
.../1-rbac/1057-rbac-role-builder.sql | 25 ++++--------
.../hsadminng/arch/ArchitectureTest.java | 2 +-
.../hsadminng/context/ContextUnitTest.java | 39 +++++--------------
.../test/ContextBasedTestWithCleanup.java | 7 ++--
.../hsadminng/hs/office/test/EntityList.java | 4 +-
.../java/net/hostsharing/test/JpaAttempt.java | 1 -
.../hostsharing/test/PatchUnitTestBase.java | 4 +-
32 files changed, 108 insertions(+), 150 deletions(-)
delete mode 100644 src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
diff --git a/.gitignore b/.gitignore
index d6a2e347..522bf4fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@
/build/www/**
/src/test/javascript/coverage/
/worktrees/
-TODO-progress.png
######################
# Node
diff --git a/README.md b/README.md
index 23209dd2..4d03a6d3 100644
--- a/README.md
+++ b/README.md
@@ -380,12 +380,6 @@ You can explore the prototype as follows:
`src/`
The actual source-code, see [Source Code Package Structure](#source-code-package-structure) for details.
-`TODO.md`
- Requirements of initial project. Do not touch!
-
-`TODO-progress.png`
- Generated diagram image of the project progress.
-
`tools/`
Some shell-scripts to useful tasks.
@@ -765,5 +759,4 @@ The output will list the generated files.
## Further Documentation
- the `doc` directory contains architecture concepts and a glossary
-- TODO.md tracks requirements and progress for the contract of the initial project,
- please do not amend anything in this document
+- the `ideas` directory contains unstructured ideas for future development or documentation
diff --git a/doc/test-concept.md b/doc/test-concept.md
index c8946342..690d1558 100644
--- a/doc/test-concept.md
+++ b/doc/test-concept.md
@@ -87,7 +87,7 @@ Acceptance-Tests run on a fully integrated and deployed system with deployed dou
Acceptance-tests, are blackbox-tests and do not count into test-code-coverage.
-TODO: Complete the Acceptance-Tests test concept.
+TODO.test: Complete the Acceptance-Tests test concept.
#### Performance-Tests
@@ -107,4 +107,4 @@ We define System-Integration-Tests as test in which this system is deployed in a
System-Integration-tests, are blackbox-tests and do not count into test-code-coverage.
-TODO: Complete the System-Integration-Tests test concept.
+TODO.test: Complete the System-Integration-Tests test concept.
diff --git a/sql/historization.sql b/sql/historization.sql
index 2f4087b4..1bd0db44 100644
--- a/sql/historization.sql
+++ b/sql/historization.sql
@@ -18,8 +18,8 @@ CREATE OR REPLACE FUNCTION historicize()
RETURNS trigger
LANGUAGE plpgsql STRICT AS $$
DECLARE
-currentUser VARCHAR(64);
- currentTask varchar;
+ currentUser VARCHAR(63);
+ currentTask VARCHAR(127);
"row" RECORD;
"alive" BOOLEAN;
"sql" varchar;
@@ -37,27 +37,27 @@ END IF;
-- determine task
currentTask = current_setting('hsadminng.currentTask');
- IF (currentTask IS NULL OR length(currentTask) < 12) THEN
- RAISE EXCEPTION 'hsadminng.currentTask (%) must be defined and min 12 characters long, please use "SET LOCAL ...;"', currentTask;
-END IF;
- RAISE NOTICE 'currentTask: %', currentTask;
+ assert currentTask IS NOT NULL AND length(currentTask) >= 12,
+ format('hsadminng.currentTask (%s) must be defined and min 12 characters long, please use "SET LOCAL ...;"', currentTask);
+ assert length(currentTask) <= 127,
+ format('hsadminng.currentTask (%s) must not be longer than 127 characters"', currentTask);
IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE') THEN
"row" := NEW;
"alive" := TRUE;
-ELSE -- DELETE or TRUNCATE
- "row" := OLD;
- "alive" := FALSE;
-END IF;
+ ELSE -- DELETE or TRUNCATE
+ "row" := OLD;
+ "alive" := FALSE;
+ END IF;
-sql := format('INSERT INTO tx_history VALUES (txid_current(), now(), %1L, %2L) ON CONFLICT DO NOTHING', currentUser, currentTask);
+ sql := format('INSERT INTO tx_history VALUES (txid_current(), now(), %1L, %2L) ON CONFLICT DO NOTHING', currentUser, currentTask);
RAISE NOTICE 'sql: %', sql;
-EXECUTE sql;
-sql := format('INSERT INTO %3$I_versions VALUES (DEFAULT, txid_current(), %1$L, %2$L, $1.*)', TG_OP, alive, TG_TABLE_NAME);
- RAISE NOTICE 'sql: %', sql;
-EXECUTE sql USING "row";
+ EXECUTE sql;
+ sql := format('INSERT INTO %3$I_versions VALUES (DEFAULT, txid_current(), %1$L, %2$L, $1.*)', TG_OP, alive, TG_TABLE_NAME);
+ RAISE NOTICE 'sql: %', sql;
+ EXECUTE sql USING "row";
-RETURN "row";
+ RETURN "row";
END; $$;
CREATE OR REPLACE PROCEDURE create_historical_view(baseTable varchar)
diff --git a/src/main/java/net/hostsharing/hsadminng/context/Context.java b/src/main/java/net/hostsharing/hsadminng/context/Context.java
index 2730147d..9a5084f0 100644
--- a/src/main/java/net/hostsharing/hsadminng/context/Context.java
+++ b/src/main/java/net/hostsharing/hsadminng/context/Context.java
@@ -55,16 +55,15 @@ public class Context {
final String currentRequest,
final String currentUser,
final String assumedRoles) {
- final var query = em.createNativeQuery(
- """
- call defineContext(
- cast(:currentTask as varchar),
- cast(:currentRequest as varchar),
- cast(:currentUser as varchar),
- cast(:assumedRoles as varchar));
- """);
- query.setParameter("currentTask", shortenToMaxLength(currentTask, 96));
- query.setParameter("currentRequest", shortenToMaxLength(currentRequest, 512)); // TODO.spec: length?
+ final var query = em.createNativeQuery("""
+ call defineContext(
+ cast(:currentTask as varchar(127)),
+ cast(:currentRequest as text),
+ cast(:currentUser as varchar(63)),
+ cast(:assumedRoles as varchar(1023)));
+ """);
+ query.setParameter("currentTask", shortenToMaxLength(currentTask, 127));
+ query.setParameter("currentRequest", currentRequest);
query.setParameter("currentUser", currentUser);
query.setParameter("assumedRoles", assumedRoles != null ? assumedRoles : "");
query.executeUpdate();
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
index 99bb50ea..6542084e 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java
@@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.bankaccount;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("BankAccount")
-public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
+public class HsOfficeBankAccountEntity implements RbacObject, Stringifyable {
private static Stringify toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
.withIdProp(HsOfficeBankAccountEntity::getIban)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
index 4927b4bc..1ce3a557 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java
@@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("Contact")
-public class HsOfficeContactEntity implements Stringifyable, HasUuid {
+public class HsOfficeContactEntity implements Stringifyable, RbacObject {
private static Stringify toString = stringify(HsOfficeContactEntity.class, "contact")
.withProp(Fields.label, HsOfficeContactEntity::getLabel)
@@ -43,13 +43,13 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
private String label;
@Column(name = "postaladdress")
- private String postalAddress; // TODO: check if we really want multiple, if so: JSON-Array or Postgres-Array?
+ private String postalAddress; // TODO.spec: check if we really want multiple, if so: JSON-Array or Postgres-Array?
@Column(name = "emailaddresses", columnDefinition = "json")
- private String emailAddresses; // TODO: check if we can really add multiple. format: ["eins@...", "zwei@..."]
+ private String emailAddresses; // TODO.spec: check if we can really add multiple. format: ["eins@...", "zwei@..."]
@Column(name = "phonenumbers", columnDefinition = "json")
- private String phoneNumbers; // TODO: check if we can really add multiple. format: { "office": "+49 40 12345-10", "fax": "+49 40 12345-05" }
+ private String phoneNumbers; // TODO.spec: check if we can really add multiple. format: { "office": "+49 40 12345-10", "fax": "+49 40 12345-05" }
@Override
public String toString() {
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
index af2ea582..cf8e2adf 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
@@ -8,7 +8,8 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@@ -25,6 +26,7 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
import java.io.IOException;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Optional;
@@ -50,7 +52,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopAssetsTransaction")
-public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
+public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacObject {
private static Stringify stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
index c62c1605..8e8d32e5 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
@@ -7,7 +7,9 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -23,7 +25,6 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.io.IOException;
-import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
@@ -47,7 +48,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopShareTransaction")
-public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid {
+public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject {
private static Stringify stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
index 0a63d0b1..08c70f66 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java
@@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -43,7 +43,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Debitor")
-public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
+public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@@ -153,7 +153,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
"vatCountryCode",
"vatBusiness",
"vatReverseCharge",
- "defaultPrefix" /* TODO: do we want that updatable? */)
+ "defaultPrefix" /* TODO.spec: do we want that updatable? */)
.toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationEntity.class,
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index 801d9033..0e6560db 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -5,7 +5,7 @@ import com.vladmihalcea.hibernate.type.range.Range;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@@ -30,6 +30,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
+import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@@ -42,7 +43,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Membership")
-public class HsOfficeMembershipEntity implements HasUuid, Stringifyable {
+public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
public static final String MEMBER_NUMBER_TAG = "M-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
index a18dbc77..6fae8dc0 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java
@@ -2,7 +2,7 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -26,7 +26,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("PartnerDetails")
-public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
+public class HsOfficePartnerDetailsEntity implements RbacObject, Stringifyable {
private static Stringify stringify = stringify(
HsOfficePartnerDetailsEntity.class,
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
index 7c9346ea..43b78fca 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java
@@ -8,7 +8,7 @@ import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@@ -45,7 +45,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Partner")
-public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
+public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
public static final String PARTNER_NUMBER_TAG = "P-";
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
index d334c741..6594cb1b 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java
@@ -11,7 +11,7 @@ public interface HsOfficePartnerRepository extends Repository findByUuid(UUID id);
- List findAll(); // TODO: move to a repo in test sources
+ List findAll(); // TODO.impl: move to a repo in test sources
@Query("""
SELECT partner FROM HsOfficePartnerEntity partner
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
index e8865ce5..4d07790d 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java
@@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.person;
import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("Person")
-public class HsOfficePersonEntity implements HasUuid, Stringifyable {
+public class HsOfficePersonEntity implements RbacObject, Stringifyable {
private static Stringify toString = stringify(HsOfficePersonEntity.class, "person")
.withProp(Fields.personType, HsOfficePersonEntity::getPersonType)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
index 2077cf4a..8d6c6fe8 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relation/HsOfficeRelationEntity.java
@@ -4,7 +4,7 @@ import lombok.*;
import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
@@ -32,7 +32,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
-public class HsOfficeRelationEntity implements HasUuid, Stringifyable {
+public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
private static Stringify toString = stringify(HsOfficeRelationEntity.class, "rel")
.withProp(Fields.anchor, HsOfficeRelationEntity::getAnchor)
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index 403e2972..6ae8ff64 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -7,7 +7,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@@ -37,7 +37,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("SEPA-Mandate")
-public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
+public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject {
private static Stringify stringify = stringify(HsOfficeSepaMandateEntity.class)
.withProp(e -> e.getBankAccount().getIban())
diff --git a/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java b/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
deleted file mode 100644
index 03e6abf3..00000000
--- a/src/main/java/net/hostsharing/hsadminng/persistence/HasUuid.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.hostsharing.hsadminng.persistence;
-
-import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
-
-// TODO: remove this interface, I just wanted to avoid to many changes in that PR
-public interface HasUuid extends RbacObject {
-}
diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
index 6bba2b12..cb048455 100644
--- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
+++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RbacView.java
@@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
import net.hostsharing.hsadminng.test.dom.TestDomainEntity;
@@ -277,7 +277,7 @@ public class RbacView {
*/
public RbacView importRootEntityAliasProxy(
final String aliasName,
- final Class extends HasUuid> entityClass,
+ final Class extends RbacObject> entityClass,
final SQL fetchSql,
final Column dependsOnColum) {
if (rootEntityAliasProxy != null) {
@@ -300,7 +300,7 @@ public class RbacView {
* a JPA entity class extending RbacObject
*/
public RbacView importSubEntityAlias(
- final String aliasName, final Class extends HasUuid> entityClass,
+ final String aliasName, final Class extends RbacObject> entityClass,
final SQL fetchSql, final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, true, NOT_NULL);
return this;
@@ -334,7 +334,7 @@ public class RbacView {
* a JPA entity class extending RbacObject
*/
public RbacView importEntityAlias(
- final String aliasName, final Class extends HasUuid> entityClass,
+ final String aliasName, final Class extends RbacObject> entityClass,
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
return this;
@@ -342,14 +342,14 @@ public class RbacView {
// TODO: remove once it's not used in HsOffice...Entity anymore
public RbacView importEntityAlias(
- final String aliasName, final Class extends HasUuid> entityClass,
+ final String aliasName, final Class extends RbacObject> entityClass,
final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, directlyFetchedByDependsOnColumn(), dependsOnColum, false, null);
return this;
}
private EntityAlias importEntityAliasImpl(
- final String aliasName, final Class extends HasUuid> entityClass,
+ final String aliasName, final Class extends RbacObject> entityClass,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
entityAliases.put(aliasName, entityAlias);
@@ -1046,7 +1046,7 @@ public class RbacView {
}
}
- private static void generateRbacView(final Class extends HasUuid> c) {
+ private static void generateRbacView(final Class extends RbacObject> c) {
final Method mainMethod = stream(c.getMethods()).filter(
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
diff --git a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
index 94caa1de..19340440 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/cust/TestCustomerEntity.java
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
@@ -24,7 +24,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
-public class TestCustomerEntity implements HasUuid {
+public class TestCustomerEntity implements RbacObject {
@Id
@GeneratedValue
diff --git a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
index d3d387d7..b6d659c5 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/dom/TestDomainEntity.java
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.test.pac.TestPackageEntity;
@@ -26,7 +26,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
-public class TestDomainEntity implements HasUuid {
+public class TestDomainEntity implements RbacObject {
@Id
@GeneratedValue
diff --git a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
index 3ac28f34..e8430863 100644
--- a/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/test/pac/TestPackageEntity.java
@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
@@ -26,7 +26,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
-public class TestPackageEntity implements HasUuid {
+public class TestPackageEntity implements RbacObject {
@Id
@GeneratedValue
diff --git a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
index 45736dc3..4e5b5f4d 100644
--- a/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
+++ b/src/main/resources/api-definition/rbac/rbac-role-schemas.yaml
@@ -23,7 +23,7 @@ components:
- ADMIN
- AGENT
- TENANT
- - GUEST
- REFERRER
+ - GUEST
roleName:
type: string
diff --git a/src/main/resources/db/changelog/0-basis/010-context.sql b/src/main/resources/db/changelog/0-basis/010-context.sql
index 3bb37037..8ea73f45 100644
--- a/src/main/resources/db/changelog/0-basis/010-context.sql
+++ b/src/main/resources/db/changelog/0-basis/010-context.sql
@@ -10,10 +10,10 @@
This function will be overwritten by later changesets.
*/
create procedure contextDefined(
- currentTask varchar,
- currentRequest varchar,
- currentUser varchar,
- assumedRoles varchar
+ currentTask varchar(127),
+ currentRequest text,
+ currentUser varchar(63),
+ assumedRoles varchar(1023)
)
language plpgsql as $$
begin
diff --git a/src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql b/src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql
index faae1782..ab3a9bd5 100644
--- a/src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql
+++ b/src/main/resources/db/changelog/1-rbac/1054-rbac-context.sql
@@ -85,10 +85,10 @@ end; $$;
This function will be overwritten by later changesets.
*/
create or replace procedure contextDefined(
- currentTask varchar,
- currentRequest varchar,
- currentUser varchar,
- assumedRoles varchar
+ currentTask varchar(127),
+ currentRequest text,
+ currentUser varchar(63),
+ assumedRoles varchar(1023)
)
language plpgsql as $$
declare
diff --git a/src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql b/src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql
index 57a97a2f..57ba3cb7 100644
--- a/src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql
+++ b/src/main/resources/db/changelog/1-rbac/1057-rbac-role-builder.sql
@@ -1,18 +1,5 @@
--liquibase formatted sql
--- ============================================================================
--- PERMISSIONS
---changeset rbac-role-builder-to-uuids:1 endDelimiter:--//
--- ----------------------------------------------------------------------------
-
-create or replace function toPermissionUuids(forObjectUuid uuid, permitOps RbacOp[])
- returns uuid[]
- language plpgsql
- strict as $$
-begin
- return createPermissions(forObjectUuid, permitOps);
-end; $$;
-
-- =================================================================
-- CREATE ROLE
@@ -32,6 +19,8 @@ create or replace function createRoleWithGrants(
language plpgsql as $$
declare
roleUuid uuid;
+ permission RbacOp;
+ permissionUuid uuid;
subRoleDesc RbacRoleDescriptor;
superRoleDesc RbacRoleDescriptor;
subRoleUuid uuid;
@@ -41,9 +30,11 @@ declare
begin
roleUuid := createRole(roleDescriptor);
- if cardinality(permissions) > 0 then
- call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
- end if;
+ foreach permission in array permissions
+ loop
+ permissionUuid := createPermission(roleDescriptor.objectuuid, permission);
+ call grantPermissionToRole(permissionUuid, roleUuid);
+ end loop;
foreach superRoleDesc in array array_remove(incomingSuperRoles, null)
loop
@@ -60,7 +51,7 @@ begin
if cardinality(userUuids) > 0 then
-- direct grants to users need a grantedByRole which can revoke the grant
if grantedByRole is null then
- userGrantsByRoleUuid := roleUuid; -- TODO: or do we want to require an explicit userGrantsByRoleUuid?
+ userGrantsByRoleUuid := roleUuid; -- TODO.spec: or do we want to require an explicit userGrantsByRoleUuid?
else
userGrantsByRoleUuid := getRoleId(grantedByRole);
end if;
diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
index be612e90..497c60de 100644
--- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java
@@ -121,7 +121,7 @@ public class ArchitectureTest {
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage(
"..hs.office.(*)..",
- "..rbac.rbacgrant" // TODO: just because of RbacGrantsDiagramServiceIntegrationTest
+ "..rbac.rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest
);
@ArchTest
diff --git a/src/test/java/net/hostsharing/hsadminng/context/ContextUnitTest.java b/src/test/java/net/hostsharing/hsadminng/context/ContextUnitTest.java
index af78c76a..2104f297 100644
--- a/src/test/java/net/hostsharing/hsadminng/context/ContextUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/context/ContextUnitTest.java
@@ -27,12 +27,12 @@ import static org.mockito.Mockito.verify;
class ContextUnitTest {
private static final String DEFINE_CONTEXT_QUERY_STRING = """
- call defineContext(
- cast(:currentTask as varchar),
- cast(:currentRequest as varchar),
- cast(:currentUser as varchar),
- cast(:assumedRoles as varchar));
- """;
+ call defineContext(
+ cast(:currentTask as varchar(127)),
+ cast(:currentRequest as text),
+ cast(:currentUser as varchar(63)),
+ cast(:assumedRoles as varchar(1023)));
+ """;
@Nested
class WithoutHttpRequest {
@@ -71,7 +71,7 @@ class ContextUnitTest {
context.define("current-user");
verify(em).createNativeQuery(DEFINE_CONTEXT_QUERY_STRING);
- verify(nativeQuery).setParameter("currentRequest", "");
+ verify(nativeQuery).setParameter("currentRequest", null);
}
}
@@ -142,8 +142,8 @@ class ContextUnitTest {
}
@Test
- void shortensCurrentTaskTo96Chars() throws IOException {
- givenRequest("GET", "http://localhost:9999/api/endpoint/" + "0123456789".repeat(10),
+ void shortensCurrentTaskToMaxLength() throws IOException {
+ givenRequest("GET", "http://localhost:9999/api/endpoint/" + "0123456789".repeat(13),
Map.ofEntries(
Map.entry("current-user", "given-user"),
Map.entry("content-type", "application/json"),
@@ -153,26 +153,7 @@ class ContextUnitTest {
context.define("current-user");
verify(em).createNativeQuery(DEFINE_CONTEXT_QUERY_STRING);
- verify(nativeQuery).setParameter(eq("currentTask"), argThat((String t) -> t.length() == 96));
- }
-
- @Test
- void shortensCurrentRequestTo512Chars() throws IOException {
- givenRequest("GET", "http://localhost:9999/api/endpoint",
- Map.ofEntries(
- Map.entry("current-user", "given-user"),
- Map.entry("content-type", "application/json"),
- Map.entry("user-agent", "given-user-agent")),
- """
- {
- "dummy": "%s"
- }
- """.formatted("0123456789".repeat(60)));
-
- context.define("current-user");
-
- verify(em).createNativeQuery(DEFINE_CONTEXT_QUERY_STRING);
- verify(nativeQuery).setParameter(eq("currentRequest"), argThat((String t) -> t.length() == 512));
+ verify(nativeQuery).setParameter(eq("currentTask"), argThat((String t) -> t.length() == 127));
}
private void givenRequest(final String method, final String url, final Map headers, final String body)
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
index 722fd87e..fc0b81c3 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/test/ContextBasedTestWithCleanup.java
@@ -1,11 +1,10 @@
package net.hostsharing.hsadminng.hs.office.test;
import net.hostsharing.hsadminng.context.ContextBasedTest;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
-import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleEntity;
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
import net.hostsharing.test.JpaAttempt;
@@ -66,7 +65,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return merged;
}
- public UUID toCleanup(final Class extends HasUuid> entityClass, final UUID uuidToCleanup) {
+ public UUID toCleanup(final Class extends RbacObject> entityClass, final UUID uuidToCleanup) {
out.println("toCleanup(" + entityClass.getSimpleName() + ", " + uuidToCleanup);
entitiesToCleanup.put(uuidToCleanup, entityClass);
return uuidToCleanup;
@@ -81,7 +80,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
return entity;
}
- protected void cleanupAllNew(final Class extends HasUuid> entityClass) {
+ protected void cleanupAllNew(final Class extends RbacObject> entityClass) {
if (initialRbacObjects == null) {
out.println("skipping cleanupAllNew: " + entityClass.getSimpleName());
return; // TODO: seems @AfterEach is called without any @BeforeEach
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java b/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
index 1699a5d2..2cc55e61 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/test/EntityList.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.test;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import java.util.List;
@@ -8,7 +8,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class EntityList {
- public static E one(final List entities) {
+ public static E one(final List entities) {
assertThat(entities).hasSize(1);
return entities.stream().findFirst().orElseThrow();
}
diff --git a/src/test/java/net/hostsharing/test/JpaAttempt.java b/src/test/java/net/hostsharing/test/JpaAttempt.java
index 86a332cd..d0ddd040 100644
--- a/src/test/java/net/hostsharing/test/JpaAttempt.java
+++ b/src/test/java/net/hostsharing/test/JpaAttempt.java
@@ -130,7 +130,6 @@ public class JpaAttempt {
final Class extends RuntimeException> expectedExceptionClass,
final String... expectedRootCauseMessages) {
assertThat(wasSuccessful()).as("wasSuccessful").isFalse();
- // TODO: also check the expected exception class itself
final String firstRootCauseMessageLine = firstRootCauseMessageLineOf(caughtException(expectedExceptionClass));
for (String expectedRootCauseMessage : expectedRootCauseMessages) {
assertThat(firstRootCauseMessageLine).contains(expectedRootCauseMessage);
diff --git a/src/test/java/net/hostsharing/test/PatchUnitTestBase.java b/src/test/java/net/hostsharing/test/PatchUnitTestBase.java
index ce7ff865..56f97938 100644
--- a/src/test/java/net/hostsharing/test/PatchUnitTestBase.java
+++ b/src/test/java/net/hostsharing/test/PatchUnitTestBase.java
@@ -1,6 +1,6 @@
package net.hostsharing.test;
-import net.hostsharing.hsadminng.persistence.HasUuid;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.mapper.EntityPatcher;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
@@ -233,7 +233,7 @@ public abstract class PatchUnitTestBase {
}
}
- protected static class JsonNullableProperty extends Property {
+ protected static class JsonNullableProperty extends Property {
private final BiConsumer> resourceSetter;
public final RV givenPatchValue;
From 73c378b456018e38b51b230df2e7634963fa8d39 Mon Sep 17 00:00:00 2001
From: Michael Hoennig
Date: Tue, 2 Apr 2024 13:24:25 +0200
Subject: [PATCH 12/12] spring-boot-3-2-upgrade (#32)
Co-authored-by: Michael Hoennig
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/32
Reviewed-by: Timotheus Pokorra
---
build.gradle | 34 ++++-------
doc/rbac.md | 2 +-
etc/owasp-dependency-check-suppression.xml | 48 ---------------
settings.gradle | 24 --------
.../hsadminng/context/Context.java | 9 +--
.../RestResponseEntityExceptionHandler.java | 28 ++++++++-
...OfficeCoopAssetsTransactionController.java | 3 +-
.../HsOfficeCoopAssetsTransactionEntity.java | 2 +-
...OfficeCoopSharesTransactionController.java | 3 +-
.../HsOfficeCoopSharesTransactionEntity.java | 4 +-
.../HsOfficeMembershipController.java | 3 +-
.../membership/HsOfficeMembershipEntity.java | 4 +-
.../HsOfficeSepaMandateController.java | 3 +-
.../HsOfficeSepaMandateEntity.java | 4 +-
.../hsadminng/mapper/PostgresArray.java | 58 -------------------
.../hsadminng/mapper/PostgresDateRange.java | 2 +-
...iceMembershipControllerAcceptanceTest.java | 2 +-
...OfficeMembershipEntityPatcherUnitTest.java | 2 +-
.../HsOfficeMembershipEntityUnitTest.java | 2 +-
...ceMembershipRepositoryIntegrationTest.java | 2 +-
.../office/membership/TestHsMembership.java | 2 +-
...ceSepaMandateControllerAcceptanceTest.java | 2 +-
...fficeSepaMandateEntityPatcherUnitTest.java | 2 +-
...eSepaMandateRepositoryIntegrationTest.java | 2 +-
.../mapper/PostgresArrayIntegrationTest.java | 13 +----
25 files changed, 62 insertions(+), 198 deletions(-)
delete mode 100644 src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java
diff --git a/build.gradle b/build.gradle
index 6539242e..88c59050 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,15 +1,15 @@
plugins {
id 'java'
- id 'org.springframework.boot' version '3.1.7'
+ id 'org.springframework.boot' version '3.2.4'
id 'io.spring.dependency-management' version '1.1.4'
id 'io.openapiprocessor.openapi-processor' version '2023.2'
- id 'com.github.jk1.dependency-license-report' version '2.5'
- id "org.owasp.dependencycheck" version "9.0.7"
- id "com.diffplug.spotless" version "6.23.3"
+ id 'com.github.jk1.dependency-license-report' version '2.6'
+ id "org.owasp.dependencycheck" version "9.0.10"
+ id "com.diffplug.spotless" version "6.25.0"
id 'jacoco'
id 'info.solidsoft.pitest' version '1.15.0'
id 'se.patrikerdes.use-latest-versions' version '0.2.18'
- id 'com.github.ben-manes.versions' version '0.50.0'
+ id 'com.github.ben-manes.versions' version '0.51.0'
}
group = 'net.hostsharing'
@@ -59,28 +59,16 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
- implementation 'org.springdoc:springdoc-openapi:2.3.0'
- implementation 'org.postgresql:postgresql:42.7.1'
- implementation 'org.liquibase:liquibase-core:4.25.1'
- implementation 'com.vladmihalcea:hibernate-types-60:2.21.1'
- implementation 'io.hypersistence:hypersistence-utils-hibernate-62:3.7.0'
- implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1'
+ implementation 'org.springdoc:springdoc-openapi:2.4.0'
+ implementation 'org.postgresql:postgresql:42.7.3'
+ implementation 'org.liquibase:liquibase-core:4.27.0'
+ implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.3'
+ implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
implementation 'org.apache.commons:commons-text:1.11.0'
implementation 'org.modelmapper:modelmapper:3.2.0'
implementation 'org.iban4j:iban4j:3.2.7-RELEASE'
- implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
-
- // fixes vulnerability CVE-2022-1471
- // The dependency usually comes from Spring Boot, just in the wrong version.
- // TODO: Remove this explicit dependency once we are on SpringBoot 3.2.x
- // as well as the related exclude in settings.gradle
- // and the dependency suppression in owasp-dependency-check-suppression.xml.
- implementation('org.yaml:snakeyaml') {
- version {
- strictly('2.2')
- }
- }
+ implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'
compileOnly 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
diff --git a/doc/rbac.md b/doc/rbac.md
index 9e562148..662bed29 100644
--- a/doc/rbac.md
+++ b/doc/rbac.md
@@ -694,7 +694,7 @@ Users can view only the roles to which are granted to them.
Grant can be `empowered`, this means that the grantee user can grant the granted role to other users
and revoke grants to that role.
-(TODO: access control part not yet implemented)
+(TODO: access control part not yet implemented, currently all accessible roles can be granted to other users)
Grants can be `managed`, which means they are created and deleted by system-defined rules.
If a grant is not managed, it was created by an empowered user and can be deleted by empowered users.
diff --git a/etc/owasp-dependency-check-suppression.xml b/etc/owasp-dependency-check-suppression.xml
index 39d77b47..af4269d4 100644
--- a/etc/owasp-dependency-check-suppression.xml
+++ b/etc/owasp-dependency-check-suppression.xml
@@ -1,33 +1,5 @@
-
-
- ^pkg:maven/org\.springframework/spring-web@.*$
- CVE-2016-1000027
-
-
-
- ^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$
- CVE-2022-42003
-
-
-
- ^pkg:maven/org\.eclipse\.angus/angus\-activation@.*$
- cpe:/a:eclipse:eclipse_ide
-
-
-
- ^pkg:maven/jakarta\.activation/jakarta\.activation\-api@.*$
- cpe:/a:eclipse:eclipse_ide
-
^pkg:maven/com\.fasterxml\.jackson\.core/jackson\-databind@.*$
cpe:/a:fasterxml:jackson-databind
-
-
- ^pkg:maven/com\.jayway\.jsonpath/json\-path@.*$
- CVE-2023-51074
-
^pkg:maven/org\.pitest/pitest\-command\-line@.*$
cpe:/a:line:line
-
-
- ^pkg:maven/org\.yaml/snakeyaml@.*$
- CVE-2022-1471
-
diff --git a/settings.gradle b/settings.gradle
index 09d09d6f..d6f3f9eb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -11,28 +11,4 @@ plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
}
-dependencyResolutionManagement {
- components {
- all {
- allVariants {
- withDependencies {
- removeAll {
- // Spring Boot 3.1.x has a transient dependency to snakeyaml 1.3
- // which contains a severe vulnerability.
- // Here we remove this transient dependency and in build.gradle
- // we add an explicit dependency to snakeyaml 2.2,
- // which does not have this vulnerability anymore.
- //
- // TODO: Check Once we are on SpringBoot 3.2.x, check if this exclude
- // is still neccessary. If not:
- // Remove it // as well as the related explicit dependency in build.gradle
- // and the dependency suppression in owasp-dependency-check-suppression.xml.
- it.module in [ 'snakeyaml' ]
- }
- }
- }
- }
- }
-}
-
rootProject.name = 'hsadmin-ng'
diff --git a/src/main/java/net/hostsharing/hsadminng/context/Context.java b/src/main/java/net/hostsharing/hsadminng/context/Context.java
index 9a5084f0..b3dac96b 100644
--- a/src/main/java/net/hostsharing/hsadminng/context/Context.java
+++ b/src/main/java/net/hostsharing/hsadminng/context/Context.java
@@ -15,11 +15,9 @@ import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
-import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.function.Predicate.not;
-import static net.hostsharing.hsadminng.mapper.PostgresArray.fromPostgresArray;
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
@Service
@@ -82,14 +80,11 @@ public class Context {
}
public String[] getAssumedRoles() {
- final byte[] result = (byte[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
- return fromPostgresArray(result, String.class, Function.identity());
+ return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
}
public UUID[] currentSubjectsUuids() {
- final byte[] result = (byte[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class)
- .getSingleResult();
- return fromPostgresArray(result, UUID.class, UUID::fromString);
+ return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
}
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {
diff --git a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java
index 6c36dfb8..5d675484 100644
--- a/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java
+++ b/src/main/java/net/hostsharing/hsadminng/errors/RestResponseEntityExceptionHandler.java
@@ -11,16 +11,18 @@ import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaSystemException;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.method.ParameterValidationResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.ValidationException;
-import java.util.NoSuchElementException;
-import java.util.Optional;
+import java.util.*;
import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
@@ -119,6 +121,28 @@ public class RestResponseEntityExceptionHandler
return errorResponse(request, HttpStatus.BAD_REQUEST, errorList.toString());
}
+ @SuppressWarnings("unchecked,rawtypes")
+
+ @Override
+ protected ResponseEntity handleHandlerMethodValidationException(
+ final HandlerMethodValidationException exc,
+ final HttpHeaders headers,
+ final HttpStatusCode status,
+ final WebRequest request) {
+ final var errorList = exc
+ .getAllValidationResults()
+ .stream()
+ .map(ParameterValidationResult::getResolvableErrors)
+ .flatMap(Collection::stream)
+ .filter(FieldError.class::isInstance)
+ .map(FieldError.class::cast)
+ .map(fieldError -> fieldError.getField() + " " + fieldError.getDefaultMessage() + " but is \""
+ + fieldError.getRejectedValue() + "\"")
+ .toList();
+ return errorResponse(request, HttpStatus.BAD_REQUEST, errorList.toString());
+ }
+
+
private String userReadableEntityClassName(final String exceptionMessage) {
final var regex = "(net.hostsharing.hsadminng.[a-z0-9_.]*.[A-Za-z0-9_$]*Entity) ";
final var pattern = Pattern.compile(regex);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java
index 946b4626..add8333c 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionController.java
@@ -13,7 +13,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
-import jakarta.validation.Valid;
import jakarta.validation.ValidationException;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -59,7 +58,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
public ResponseEntity addCoopAssetsTransaction(
final String currentUser,
final String assumedRoles,
- @Valid final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
+ final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
context.define(currentUser, assumedRoles);
validate(requestBody);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
index cf8e2adf..47fd03a6 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java
@@ -10,7 +10,7 @@ import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
-import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
+import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java
index 813d8b92..39dc9002 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionController.java
@@ -13,7 +13,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
-import jakarta.validation.Valid;
import jakarta.validation.ValidationException;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -60,7 +59,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
public ResponseEntity addCoopSharesTransaction(
final String currentUser,
final String assumedRoles,
- @Valid final HsOfficeCoopSharesTransactionInsertResource requestBody) {
+ final HsOfficeCoopSharesTransactionInsertResource requestBody) {
context.define(currentUser, assumedRoles);
validate(requestBody);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
index 8e8d32e5..8ab19435 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java
@@ -7,10 +7,8 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
+import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
-import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
-import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
-import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java
index 540ba2a2..3c783aae 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java
@@ -12,7 +12,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
-import jakarta.validation.Valid;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
@@ -53,7 +52,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
public ResponseEntity addMembership(
final String currentUser,
final String assumedRoles,
- @Valid final HsOfficeMembershipInsertResource body) {
+ final HsOfficeMembershipInsertResource body) {
context.define(currentUser, assumedRoles);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
index 0e6560db..c486dc92 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java
@@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
+import io.hypersistence.utils.hibernate.type.range.Range;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
index 364f4ba4..115b8948 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateController.java
@@ -14,7 +14,6 @@ import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBui
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
-import jakarta.validation.Valid;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
@@ -57,7 +56,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
public ResponseEntity addSepaMandate(
final String currentUser,
final String assumedRoles,
- @Valid final HsOfficeSepaMandateInsertResource body) {
+ final HsOfficeSepaMandateInsertResource body) {
context.define(currentUser, assumedRoles);
diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
index 6ae8ff64..ac831295 100644
--- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
+++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java
@@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.hs.office.sepamandate;
-import com.vladmihalcea.hibernate.type.range.PostgreSQLRangeType;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
+import io.hypersistence.utils.hibernate.type.range.Range;
import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java b/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java
deleted file mode 100644
index e1e1d056..00000000
--- a/src/main/java/net/hostsharing/hsadminng/mapper/PostgresArray.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package net.hostsharing.hsadminng.mapper;
-
-import lombok.experimental.UtilityClass;
-import org.postgresql.util.PGtokenizer;
-
-import java.lang.reflect.Array;
-import java.nio.charset.StandardCharsets;
-import java.util.function.Function;
-
-@UtilityClass
-public class PostgresArray {
-
- /**
- * Converts a byte[], as returned for a Postgres-array by native queries, to a Java array.
- *
- * This example code worked with Hibernate 5 (Spring Boot 3.0.x):
- *
- * return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
- *
- *
- *
- * With Hibernate 6 (Spring Boot 3.1.x), this utility method can be used like such:
- *
- * final byte[] result = (byte[]) em.createNativeQuery("select * from currentSubjectsUuids() as uuids", UUID[].class)
- * .getSingleResult();
- * return fromPostgresArray(result, UUID.class, UUID::fromString);
- *
- *
- *
- * @param pgArray the byte[] returned by a native query containing as rendered for a Postgres array
- * @param elementClass the class of a single element of the Java array to be returned
- * @param itemParser converts a string element to the specified elementClass
- * @return a Java array containing the data from pgArray
- * @param type of a single element of the Java array
- */
- public static T[] fromPostgresArray(final byte[] pgArray, final Class elementClass, final Function itemParser) {
- final var pgArrayLiteral = new String(pgArray, StandardCharsets.UTF_8);
- if (pgArrayLiteral.length() == 2) {
- return newGenericArray(elementClass, 0);
- }
- final PGtokenizer tokenizer = new PGtokenizer(pgArrayLiteral.substring(1, pgArrayLiteral.length()-1), ',');
- tokenizer.remove("\"", "\"");
- final T[] array = newGenericArray(elementClass, tokenizer.getSize()); // Create a new array of the specified type and length
- for ( int n = 0; n < tokenizer.getSize(); ++n ) {
- final String token = tokenizer.getToken(n);
- if ( !"NULL".equals(token) ) {
- array[n] = itemParser.apply(token.trim().replace("\\\"", "\""));
- }
- }
- return array;
- }
-
- @SuppressWarnings("unchecked")
- private static T[] newGenericArray(final Class elementClass, final int length) {
- return (T[]) Array.newInstance(elementClass, length);
- }
-
-}
diff --git a/src/main/java/net/hostsharing/hsadminng/mapper/PostgresDateRange.java b/src/main/java/net/hostsharing/hsadminng/mapper/PostgresDateRange.java
index c360db1a..db6ad189 100644
--- a/src/main/java/net/hostsharing/hsadminng/mapper/PostgresDateRange.java
+++ b/src/main/java/net/hostsharing/hsadminng/mapper/PostgresDateRange.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.mapper;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import lombok.experimental.UtilityClass;
import java.time.LocalDate;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
index f3601449..5ff5c032 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.HsadminNgApplication;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
index b691095b..ddad360e 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeReasonForTerminationResource;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
index 1c4d2dc6..ef47eaa0 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import org.junit.jupiter.api.Test;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
index 1659c929..633278a0 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java
index ff50eb58..857e9369 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.membership;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import java.time.LocalDate;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
index ad94ca9d..33a6810a 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.sepamandate;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import net.hostsharing.hsadminng.HsadminNgApplication;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityPatcherUnitTest.java
index 05f4ca07..04ba4fee 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityPatcherUnitTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntityPatcherUnitTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.sepamandate;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeSepaMandatePatchResource;
import net.hostsharing.test.PatchUnitTestBase;
diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
index a0555579..4f558db8 100644
--- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java
@@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.hs.office.sepamandate;
-import com.vladmihalcea.hibernate.type.range.Range;
+import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
diff --git a/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java
index 8f3e95e0..3542caa1 100644
--- a/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java
+++ b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java
@@ -7,7 +7,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import jakarta.persistence.EntityManager;
import java.util.UUID;
-import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
@@ -30,9 +29,7 @@ class PostgresArrayIntegrationTest {
return emptyArray;
end; $$;
""").executeUpdate();
- final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnEmptyArray()", String[].class).getSingleResult();
-
- final String[] result = PostgresArray.fromPostgresArray(pgArray, String.class, Function.identity());
+ final String[] result = (String[]) em.createNativeQuery("SELECT returnEmptyArray()", String[].class).getSingleResult();
assertThat(result).isEmpty();
}
@@ -53,9 +50,7 @@ class PostgresArrayIntegrationTest {
return array[text1, text2, text3, null, text4];
end; $$;
""").executeUpdate();
- final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnStringArray()", String[].class).getSingleResult();
-
- final String[] result = PostgresArray.fromPostgresArray(pgArray, String.class, Function.identity());
+ final String[] result = (String[]) em.createNativeQuery("SELECT returnStringArray()", String[].class).getSingleResult();
assertThat(result).containsExactly("one", "two, three", "four; five", null, "say \"Hello\" to me");
}
@@ -75,9 +70,7 @@ class PostgresArrayIntegrationTest {
return ARRAY[uuid1, uuid2, null, uuid3];
end; $$;
""").executeUpdate();
- final byte[] pgArray = (byte[]) em.createNativeQuery("SELECT returnUuidArray()", UUID[].class).getSingleResult();
-
- final UUID[] result = PostgresArray.fromPostgresArray(pgArray, UUID.class, UUID::fromString);
+ final UUID[] result = (UUID[]) em.createNativeQuery("SELECT returnUuidArray()", UUID[].class).getSingleResult();
assertThat(result).containsExactly(
UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479"),