Compare commits
No commits in common. "76b98eab2eefec5173b7d26e1713dc755b319079" and "032ce6d16e1d72724017b9dc172bc188d80fbdc3" have entirely different histories.
76b98eab2e
...
032ce6d16e
148
doc/rbac.md
148
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.
|
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.
|
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. SELECT or UPDATE) on a specific (business-) object.
|
A permission allows a specific operation (e.g. view or edit) on a specific (business-) object.
|
||||||
|
|
||||||
You can find the entity structure as a UML class diagram as follows:
|
You can find the entity structure as a UML class diagram as follows:
|
||||||
|
|
||||||
@ -101,12 +101,13 @@ package RBAC {
|
|||||||
RbacPermission *-- RbacObject
|
RbacPermission *-- RbacObject
|
||||||
|
|
||||||
enum RbacOperation {
|
enum RbacOperation {
|
||||||
INSERT:package
|
add-package
|
||||||
INSERT:domain
|
add-domain
|
||||||
|
add-domain
|
||||||
...
|
...
|
||||||
SELECT
|
view
|
||||||
UPDATE
|
edit
|
||||||
DELETE
|
delete
|
||||||
}
|
}
|
||||||
|
|
||||||
entity RbacObject {
|
entity RbacObject {
|
||||||
@ -171,10 +172,11 @@ An *RbacPermission* allows a specific *RbacOperation* on a specific *RbacObject*
|
|||||||
An *RbacOperation* determines, <u>what</u> an *RbacPermission* allows to do.
|
An *RbacOperation* determines, <u>what</u> an *RbacPermission* allows to do.
|
||||||
It can be one of:
|
It can be one of:
|
||||||
|
|
||||||
- **'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'
|
- **'add-...'** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package"
|
||||||
- **'SELECT'** - permits selecting the row specified by the permission, is included in all other permissions
|
- **'view'** - permits reading the contents of the object specified by the permission
|
||||||
- **'UPDATE'** - permits updating (only the updatable columns of) the row specified by the permission, includes 'SELECT'
|
- **'edit'** - change the contents of the object specified by the permission
|
||||||
- **'DELETE'** - permits deleting the row specified by the permission, includes 'SELECT'
|
- **'delete'** - delete the object specified by the permission
|
||||||
|
- **'\*'**
|
||||||
|
|
||||||
This list is extensible according to the needs of the access rule system.
|
This list is extensible according to the needs of the access rule system.
|
||||||
|
|
||||||
@ -210,7 +212,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.
|
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'.
|
By this, all roles ob sub-objects, which are assigned to the 'admin' role, are also granted to the 'owner'.
|
||||||
|
|
||||||
#### admin
|
#### admin
|
||||||
@ -218,14 +220,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.
|
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.
|
E.g. a 'package' is manged by the admin of the customer.
|
||||||
|
|
||||||
Whoever has the admin-role assigned, can usually update the related business-object but not delete (or deactivating) it.
|
Whoever has the admin-role assigned, can usually edit the related business-object but not deleting (or deactivating) it.
|
||||||
|
|
||||||
The admin-role also comprises lesser roles, through which the SELECT-permission is granted.
|
The admin-role also comprises lesser roles, through which the view-permission is granted.
|
||||||
|
|
||||||
#### agent
|
#### agent
|
||||||
|
|
||||||
The agent-role is not used in the examples of this document, because it's for more complex cases.
|
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.
|
It's usually granted to those roles and users who represent the related business-object, but are not allowed to edit it.
|
||||||
|
|
||||||
Other than the tenant-role, it usually offers broader visibility of sub-business-objects (joined entities).
|
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,
|
E.g. a package-admin is allowed to see the related debitor-business-object,
|
||||||
@ -233,19 +235,19 @@ 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.
|
The tenant-role is granted to everybody who needs to be able to view the business-object and (probably some) related business-objects.
|
||||||
Usually all owners, admins and tenants of sub-objects get this role granted.
|
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.
|
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.
|
||||||
|
|
||||||
#### guest
|
#### 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.
|
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.
|
If the guest-role exists, the view-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.
|
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 SELECT-permission through the guest-role.
|
Also, if the guest-role exists, the tenant-role receives the view-permission through the guest-role.
|
||||||
|
|
||||||
|
|
||||||
### Referenced Business Objects and Role-Depreciation
|
### Referenced Business Objects and Role-Depreciation
|
||||||
@ -261,7 +263,7 @@ The admin-role of one object could be granted visibility to another object throu
|
|||||||
|
|
||||||
But not in all cases role-depreciation takes place.
|
But not in all cases role-depreciation takes place.
|
||||||
E.g. often a tenant-role is granted another tenant-role,
|
E.g. often a tenant-role is granted another tenant-role,
|
||||||
because it should be again allowed to select sub-objects.
|
because it should be again allowed to view sub-objects.
|
||||||
The same for the agent-role, often it is granted another agent-role.
|
The same for the agent-role, often it is granted another agent-role.
|
||||||
|
|
||||||
|
|
||||||
@ -295,14 +297,14 @@ package RbacRoles {
|
|||||||
RbacUsers -[hidden]> RbacRoles
|
RbacUsers -[hidden]> RbacRoles
|
||||||
|
|
||||||
package RbacPermissions {
|
package RbacPermissions {
|
||||||
object PermCustXyz_SELECT
|
object PermCustXyz_View
|
||||||
object PermCustXyz_UPDATE
|
object PermCustXyz_Edit
|
||||||
object PermCustXyz_DELETE
|
object PermCustXyz_Delete
|
||||||
object PermCustXyz_INSERT:Package
|
object PermCustXyz_AddPackage
|
||||||
object PermPackXyz00_SELECT
|
object PermPackXyz00_View
|
||||||
object PermPackXyz00_EDIT
|
object PermPackXyz00_Edit
|
||||||
object PermPackXyz00_DELETE
|
object PermPackXyz00_Delete
|
||||||
object PermPackXyz00_INSERT:USER
|
object PermPackXyz00_AddUser
|
||||||
}
|
}
|
||||||
RbacRoles -[hidden]> RbacPermissions
|
RbacRoles -[hidden]> RbacPermissions
|
||||||
|
|
||||||
@ -320,23 +322,23 @@ RoleAdministrators o..> RoleCustXyz_Owner
|
|||||||
RoleCustXyz_Owner o-> RoleCustXyz_Admin
|
RoleCustXyz_Owner o-> RoleCustXyz_Admin
|
||||||
RoleCustXyz_Admin o-> RolePackXyz00_Owner
|
RoleCustXyz_Admin o-> RolePackXyz00_Owner
|
||||||
|
|
||||||
RoleCustXyz_Owner o--> PermCustXyz_UPDATE
|
RoleCustXyz_Owner o--> PermCustXyz_Edit
|
||||||
RoleCustXyz_Owner o--> PermCustXyz_DELETE
|
RoleCustXyz_Owner o--> PermCustXyz_Delete
|
||||||
RoleCustXyz_Admin o--> PermCustXyz_SELECT
|
RoleCustXyz_Admin o--> PermCustXyz_View
|
||||||
RoleCustXyz_Admin o--> PermCustXyz_INSERT:Package
|
RoleCustXyz_Admin o--> PermCustXyz_AddPackage
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_SELECT
|
RolePackXyz00_Owner o--> PermPackXyz00_View
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_UPDATE
|
RolePackXyz00_Owner o--> PermPackXyz00_Edit
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_DELETE
|
RolePackXyz00_Owner o--> PermPackXyz00_Delete
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_INSERT:User
|
RolePackXyz00_Owner o--> PermPackXyz00_AddUser
|
||||||
|
|
||||||
PermCustXyz_SELECT o--> CustXyz
|
PermCustXyz_View o--> CustXyz
|
||||||
PermCustXyz_UPDATE o--> CustXyz
|
PermCustXyz_Edit o--> CustXyz
|
||||||
PermCustXyz_DELETE o--> CustXyz
|
PermCustXyz_Delete o--> CustXyz
|
||||||
PermCustXyz_INSERT:Package o--> CustXyz
|
PermCustXyz_AddPackage o--> CustXyz
|
||||||
PermPackXyz00_SELECT o--> PackXyz00
|
PermPackXyz00_View o--> PackXyz00
|
||||||
PermPackXyz00_UPDATE o--> PackXyz00
|
PermPackXyz00_Edit o--> PackXyz00
|
||||||
PermPackXyz00_DELETE o--> PackXyz00
|
PermPackXyz00_Delete o--> PackXyz00
|
||||||
PermPackXyz00_INSERT:User o--> PackXyz00
|
PermPackXyz00_AddUser o--> PackXyz00
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
@ -351,12 +353,12 @@ To support the RBAC system, for each business-object-table, some more artifacts
|
|||||||
|
|
||||||
Not yet implemented, but planned are these actions:
|
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 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 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 the 'INSERT' right for the parent-business-object.
|
- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has 'add-..' right to 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.
|
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 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.
|
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.
|
||||||
|
|
||||||
### Current User
|
### Current User
|
||||||
|
|
||||||
@ -456,26 +458,26 @@ allow_mixing
|
|||||||
entity "BObj customer#xyz" as boCustXyz
|
entity "BObj customer#xyz" as boCustXyz
|
||||||
|
|
||||||
together {
|
together {
|
||||||
entity "Perm customer#xyz *" as permCustomerXyzDELETE
|
entity "Perm customer#xyz *" as permCustomerXyzAll
|
||||||
permCustomerXyzDELETE --> boCustXyz
|
permCustomerXyzAll --> boCustXyz
|
||||||
|
|
||||||
entity "Perm customer#xyz INSERT:package" as permCustomerXyzINSERT:package
|
entity "Perm customer#xyz add-package" as permCustomerXyzAddPack
|
||||||
permCustomerXyzINSERT:package --> boCustXyz
|
permCustomerXyzAddPack --> boCustXyz
|
||||||
|
|
||||||
entity "Perm customer#xyz SELECT" as permCustomerXyzSELECT
|
entity "Perm customer#xyz view" as permCustomerXyzView
|
||||||
permCustomerXyzSELECT--> boCustXyz
|
permCustomerXyzView --> boCustXyz
|
||||||
}
|
}
|
||||||
|
|
||||||
entity "Role customer#xyz.tenant" as roleCustXyzTenant
|
entity "Role customer#xyz.tenant" as roleCustXyzTenant
|
||||||
roleCustXyzTenant --> permCustomerXyzSELECT
|
roleCustXyzTenant --> permCustomerXyzView
|
||||||
|
|
||||||
entity "Role customer#xyz.admin" as roleCustXyzAdmin
|
entity "Role customer#xyz.admin" as roleCustXyzAdmin
|
||||||
roleCustXyzAdmin --> roleCustXyzTenant
|
roleCustXyzAdmin --> roleCustXyzTenant
|
||||||
roleCustXyzAdmin --> permCustomerXyzINSERT:package
|
roleCustXyzAdmin --> permCustomerXyzAddPack
|
||||||
|
|
||||||
entity "Role customer#xyz.owner" as roleCustXyzOwner
|
entity "Role customer#xyz.owner" as roleCustXyzOwner
|
||||||
roleCustXyzOwner ..> roleCustXyzAdmin
|
roleCustXyzOwner ..> roleCustXyzAdmin
|
||||||
roleCustXyzOwner --> permCustomerXyzDELETE
|
roleCustXyzOwner --> permCustomerXyzAll
|
||||||
|
|
||||||
actor "Customer XYZ Admin" as actorCustXyzAdmin
|
actor "Customer XYZ Admin" as actorCustXyzAdmin
|
||||||
actorCustXyzAdmin --> roleCustXyzAdmin
|
actorCustXyzAdmin --> roleCustXyzAdmin
|
||||||
@ -485,6 +487,8 @@ roleAdmins --> roleCustXyzOwner
|
|||||||
|
|
||||||
actor "Any Hostmaster" as actorHostmaster
|
actor "Any Hostmaster" as actorHostmaster
|
||||||
actorHostmaster --> roleAdmins
|
actorHostmaster --> roleAdmins
|
||||||
|
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -523,17 +527,17 @@ allow_mixing
|
|||||||
entity "BObj package#xyz00" as boPacXyz00
|
entity "BObj package#xyz00" as boPacXyz00
|
||||||
|
|
||||||
together {
|
together {
|
||||||
entity "Perm package#xyz00 *" as permPackageXyzDELETE
|
entity "Perm package#xyz00 *" as permPackageXyzAll
|
||||||
permPackageXyzDELETE --> boPacXyz00
|
permPackageXyzAll --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 INSERT:domain" as permPacXyz00INSERT:user
|
entity "Perm package#xyz00 add-domain" as permPacXyz00AddUser
|
||||||
permPacXyz00INSERT:user --> boPacXyz00
|
permPacXyz00AddUser --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 UPDATE" as permPacXyz00UPDATE
|
entity "Perm package#xyz00 edit" as permPacXyz00Edit
|
||||||
permPacXyz00UPDATE --> boPacXyz00
|
permPacXyz00Edit --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 SELECT" as permPacXyz00SELECT
|
entity "Perm package#xyz00 view" as permPacXyz00View
|
||||||
permPacXyz00SELECT --> boPacXyz00
|
permPacXyz00View --> boPacXyz00
|
||||||
}
|
}
|
||||||
|
|
||||||
package {
|
package {
|
||||||
@ -548,11 +552,11 @@ package {
|
|||||||
entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
|
entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
|
||||||
}
|
}
|
||||||
|
|
||||||
rolePacXyz00Tenant --> permPacXyz00SELECT
|
rolePacXyz00Tenant --> permPacXyz00View
|
||||||
rolePacXyz00Tenant --> roleCustXyzTenant
|
rolePacXyz00Tenant --> roleCustXyzTenant
|
||||||
|
|
||||||
rolePacXyz00Owner --> rolePacXyz00Admin
|
rolePacXyz00Owner --> rolePacXyz00Admin
|
||||||
rolePacXyz00Owner --> permPackageXyzDELETE
|
rolePacXyz00Owner --> permPackageXyzAll
|
||||||
|
|
||||||
roleCustXyzAdmin --> rolePacXyz00Owner
|
roleCustXyzAdmin --> rolePacXyz00Owner
|
||||||
roleCustXyzAdmin --> roleCustXyzTenant
|
roleCustXyzAdmin --> roleCustXyzTenant
|
||||||
@ -560,8 +564,8 @@ roleCustXyzAdmin --> roleCustXyzTenant
|
|||||||
roleCustXyzOwner ..> roleCustXyzAdmin
|
roleCustXyzOwner ..> roleCustXyzAdmin
|
||||||
|
|
||||||
rolePacXyz00Admin --> rolePacXyz00Tenant
|
rolePacXyz00Admin --> rolePacXyz00Tenant
|
||||||
rolePacXyz00Admin --> permPacXyz00INSERT:user
|
rolePacXyz00Admin --> permPacXyz00AddUser
|
||||||
rolePacXyz00Admin --> permPacXyz00UPDATE
|
rolePacXyz00Admin --> permPacXyz00Edit
|
||||||
|
|
||||||
actor "Package XYZ00 Admin" as actorPacXyzAdmin
|
actor "Package XYZ00 Admin" as actorPacXyzAdmin
|
||||||
actorPacXyzAdmin -l-> rolePacXyz00Admin
|
actorPacXyzAdmin -l-> rolePacXyz00Admin
|
||||||
@ -620,10 +624,10 @@ Let's have a look at the two view queries:
|
|||||||
WHERE target.uuid IN (
|
WHERE target.uuid IN (
|
||||||
SELECT uuid
|
SELECT uuid
|
||||||
FROM queryAccessibleObjectUuidsOfSubjectIds(
|
FROM queryAccessibleObjectUuidsOfSubjectIds(
|
||||||
'SELECT, 'customer', currentSubjectsUuids()));
|
'view', 'customer', currentSubjectsUuids()));
|
||||||
|
|
||||||
This view should be automatically updatable.
|
This view should be automatically updatable.
|
||||||
Where, for updates, we actually have to check for 'UPDATE' instead of 'SELECT' operation, which makes it a bit more complicated.
|
Where, for updates, we actually have to check for 'edit' instead of 'view' operation, which makes it a bit more complicated.
|
||||||
|
|
||||||
With the larger dataset, the test suite initially needed over 7 seconds with this view query.
|
With the larger dataset, the test suite initially needed over 7 seconds with this view query.
|
||||||
At this point the second variant was tried.
|
At this point the second variant was tried.
|
||||||
@ -638,7 +642,7 @@ Looks like the query optimizer needed some statistics to find the best path.
|
|||||||
SELECT DISTINCT target.*
|
SELECT DISTINCT target.*
|
||||||
FROM customer AS target
|
FROM customer AS target
|
||||||
JOIN queryAccessibleObjectUuidsOfSubjectIds(
|
JOIN queryAccessibleObjectUuidsOfSubjectIds(
|
||||||
'SELECT, 'customer', currentSubjectsUuids()) AS allowedObjId
|
'view', 'customer', currentSubjectsUuids()) AS allowedObjId
|
||||||
ON target.uuid = allowedObjId;
|
ON target.uuid = allowedObjId;
|
||||||
|
|
||||||
This view cannot is not updatable automatically,
|
This view cannot is not updatable automatically,
|
||||||
@ -684,7 +688,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.
|
All roles are system-defined and cannot be created or modified by any external API.
|
||||||
|
|
||||||
Users can view only the roles to which are granted to them.
|
Users can view only the roles to which they are assigned.
|
||||||
|
|
||||||
## RbacGrant
|
## RbacGrant
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
|
|||||||
select *
|
select *
|
||||||
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
|
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
|
||||||
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
|
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
|
||||||
'DELETE'));
|
'delete'));
|
||||||
|
|
||||||
DO LANGUAGE plpgsql
|
DO LANGUAGE plpgsql
|
||||||
$$
|
$$
|
||||||
@ -34,12 +34,12 @@ $$
|
|||||||
result bool;
|
result bool;
|
||||||
BEGIN
|
BEGIN
|
||||||
userId = findRbacUser('superuser-alex@hostsharing.net');
|
userId = findRbacUser('superuser-alex@hostsharing.net');
|
||||||
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId));
|
result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'add-package'), userId));
|
||||||
IF (result) THEN
|
IF (result) THEN
|
||||||
RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
|
RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'SELECT'), userId));
|
result = (SELECT * FROM isPermissionGrantedToSubject(findEffectivePermissionId('package', 94928, 'view'), userId));
|
||||||
IF (NOT result) THEN
|
IF (NOT result) THEN
|
||||||
RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
|
RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
|
||||||
end if;
|
end if;
|
||||||
|
@ -20,7 +20,7 @@ CREATE POLICY customer_policy ON customer
|
|||||||
TO restricted
|
TO restricted
|
||||||
USING (
|
USING (
|
||||||
-- id=1000
|
-- id=1000
|
||||||
isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid())
|
isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid())
|
||||||
);
|
);
|
||||||
|
|
||||||
SET SESSION AUTHORIZATION restricted;
|
SET SESSION AUTHORIZATION restricted;
|
||||||
@ -35,7 +35,7 @@ SELECT * FROM customer;
|
|||||||
CREATE OR REPLACE RULE "_RETURN" AS
|
CREATE OR REPLACE RULE "_RETURN" AS
|
||||||
ON SELECT TO cust_view
|
ON SELECT TO cust_view
|
||||||
DO INSTEAD
|
DO INSTEAD
|
||||||
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid());
|
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'view'), currentUserUuid());
|
||||||
SELECT * from cust_view LIMIT 10;
|
SELECT * from cust_view LIMIT 10;
|
||||||
|
|
||||||
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net'));
|
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net'));
|
||||||
@ -52,7 +52,7 @@ CREATE OR REPLACE RULE "_RETURN" AS
|
|||||||
DO INSTEAD
|
DO INSTEAD
|
||||||
SELECT c.uuid, c.reference, c.prefix FROM customer AS c
|
SELECT c.uuid, c.reference, c.prefix FROM customer AS c
|
||||||
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
|
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
|
||||||
ON p.objectTable='test_customer' AND p.objectUuid=c.uuid;
|
ON p.objectTable='test_customer' AND p.objectUuid=c.uuid AND p.op in ('*', 'view');
|
||||||
GRANT ALL PRIVILEGES ON cust_view TO restricted;
|
GRANT ALL PRIVILEGES ON cust_view TO restricted;
|
||||||
|
|
||||||
SET SESSION SESSION AUTHORIZATION restricted;
|
SET SESSION SESSION AUTHORIZATION restricted;
|
||||||
@ -68,7 +68,7 @@ CREATE OR REPLACE VIEW cust_view AS
|
|||||||
SELECT c.uuid, c.reference, c.prefix
|
SELECT c.uuid, c.reference, c.prefix
|
||||||
FROM customer AS c
|
FROM customer AS c
|
||||||
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
|
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
|
||||||
ON p.objectUuid=c.uuid;
|
ON p.objectUuid=c.uuid AND p.op in ('*', 'view');
|
||||||
GRANT ALL PRIVILEGES ON cust_view TO restricted;
|
GRANT ALL PRIVILEGES ON cust_view TO restricted;
|
||||||
|
|
||||||
SET SESSION SESSION AUTHORIZATION 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
|
join RbacReference RR on g.ascendantUuid = RR.uuid
|
||||||
where g.descendantUuid in (
|
where g.descendantUuid in (
|
||||||
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'))
|
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'))
|
||||||
where objectTable='test_customer');
|
where objectTable='test_customer' and op in ('*', 'view'));
|
||||||
|
|
||||||
call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
|
call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.errors;
|
package net.hostsharing.hsadminng.errors;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ public class ReferenceNotFoundException extends RuntimeException {
|
|||||||
|
|
||||||
private final Class<?> entityClass;
|
private final Class<?> entityClass;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
public <E extends RbacObject> ReferenceNotFoundException(final Class<E> entityClass, final UUID uuid, final Throwable exc) {
|
public <E extends HasUuid> ReferenceNotFoundException(final Class<E> entityClass, final UUID uuid, final Throwable exc) {
|
||||||
super(exc);
|
super(exc);
|
||||||
this.entityClass = entityClass;
|
this.entityClass = entityClass;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
@ -4,7 +4,6 @@ import lombok.*;
|
|||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
@ -12,13 +11,8 @@ import jakarta.persistence.Entity;
|
|||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -56,28 +50,4 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RbacView rbac() {
|
|
||||||
return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class)
|
|
||||||
.withIdentityView(SQL.projection("concat(iban, ':', holder)"))
|
|
||||||
.withUpdatableColumns("holder", "iban", "bic")
|
|
||||||
|
|
||||||
.toRole("global", GUEST).grantPermission("bankAccount", INSERT)
|
|
||||||
|
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,13 @@ import lombok.*;
|
|||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
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.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -36,6 +28,7 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
|||||||
.withProp(Fields.label, HsOfficeContactEntity::getLabel)
|
.withProp(Fields.label, HsOfficeContactEntity::getLabel)
|
||||||
.withProp(Fields.emailAddresses, HsOfficeContactEntity::getEmailAddresses);
|
.withProp(Fields.emailAddresses, HsOfficeContactEntity::getEmailAddresses);
|
||||||
|
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(generator = "UUID")
|
@GeneratedValue(generator = "UUID")
|
||||||
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
|
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
|
||||||
@ -60,25 +53,4 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return label;
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,16 @@ import net.hostsharing.hsadminng.errors.DisplayName;
|
|||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
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.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.GenericGenerator;
|
import org.hibernate.annotations.GenericGenerator;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
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.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.rbacViewFor;
|
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -104,78 +94,4 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return DEBITOR_NUMBER_TAG + getDebitorNumberString();
|
return DEBITOR_NUMBER_TAG + getDebitorNumberString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RbacView rbac() {
|
|
||||||
return rbacViewFor("debitor", HsOfficeDebitorEntity.class)
|
|
||||||
.withIdentityView(SQL.query("""
|
|
||||||
SELECT debitor.uuid AS 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') as idName
|
|
||||||
FROM hs_office_debitor AS debitor
|
|
||||||
"""))
|
|
||||||
.withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
|
|
||||||
.withUpdatableColumns(
|
|
||||||
"debitorRel",
|
|
||||||
"billable",
|
|
||||||
"debitorUuid",
|
|
||||||
"refundBankAccountUuid",
|
|
||||||
"vatId",
|
|
||||||
"vatCountryCode",
|
|
||||||
"vatBusiness",
|
|
||||||
"vatReverseCharge",
|
|
||||||
"defaultPrefix" /* TODO: do we want that updatable? */)
|
|
||||||
.toRole("global", ADMIN).grantPermission("debitor", INSERT)
|
|
||||||
|
|
||||||
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationshipEntity.class,
|
|
||||||
fetchedBySql("""
|
|
||||||
SELECT *
|
|
||||||
FROM hs_office_relationship AS r
|
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.uuid = ${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
|
|
||||||
"""),
|
|
||||||
NULLABLE
|
|
||||||
)
|
|
||||||
.toRole("refundBankAccount", ADMIN).grantRole("debitorRel", AGENT)
|
|
||||||
.toRole("debitorRel", AGENT).grantRole("refundBankAccount", REFERRER)
|
|
||||||
|
|
||||||
.importEntityAlias("partnerRel", HsOfficeRelationshipEntity.class,
|
|
||||||
dependsOnColumn("debitorRelUuid"),
|
|
||||||
fetchedBySql("""
|
|
||||||
SELECT partnerRel.*
|
|
||||||
FROM hs_office_relationship AS partnerRel
|
|
||||||
JOIN hs_office_relationship AS debitorRel
|
|
||||||
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
|
|
||||||
WHERE partnerRel.relType = 'PARTNER'
|
|
||||||
AND ${REF}.debitorRelUuid = debitorRel.uuid
|
|
||||||
""")
|
|
||||||
)
|
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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.HsOfficePartnerPatchResource;
|
||||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
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.HsOfficePartnerRoleInsertResource;
|
||||||
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipRepository;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
||||||
import net.hostsharing.hsadminng.mapper.Mapper;
|
import net.hostsharing.hsadminng.mapper.Mapper;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -154,7 +154,7 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <E extends RbacObject> E ref(final Class<E> entityClass, final UUID uuid) {
|
private <E extends HasUuid> E ref(final Class<E> entityClass, final UUID uuid) {
|
||||||
try {
|
try {
|
||||||
return em.getReference(entityClass, uuid);
|
return em.getReference(entityClass, uuid);
|
||||||
} catch (final Throwable exc) {
|
} catch (final Throwable exc) {
|
||||||
|
@ -2,23 +2,14 @@ package net.hostsharing.hsadminng.hs.office.partner;
|
|||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
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.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -63,45 +54,6 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
return registrationNumber != null ? registrationNumber
|
return registrationNumber != null ? registrationNumber
|
||||||
: birthName != null ? birthName
|
: birthName != null ? birthName
|
||||||
: birthday != null ? birthday.toString()
|
: birthday != null ? birthday.toString()
|
||||||
: dateOfDeath != null ? dateOfDeath.toString()
|
: dateOfDeath != null ? dateOfDeath.toString() : "<empty details>";
|
||||||
: "<empty details>";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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")
|
|
||||||
.toRole("global", ADMIN).grantPermission("partner-details", INSERT)
|
|
||||||
|
|
||||||
.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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
|||||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
|
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.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.NotFound;
|
import org.hibernate.annotations.NotFound;
|
||||||
@ -25,16 +23,8 @@ import jakarta.persistence.Id;
|
|||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.persistence.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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 java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@ -91,37 +81,4 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return getTaggedPartnerNumber();
|
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(
|
|
||||||
"partnerRoleUuid",
|
|
||||||
"personUuid",
|
|
||||||
"contactUuid")
|
|
||||||
.toRole("global", ADMIN).grantPermission("partner", INSERT) // FIXME: global -> partnerRel.relAnchor?
|
|
||||||
|
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,13 @@ import lombok.experimental.FieldNameConstants;
|
|||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
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.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -73,28 +65,4 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
|||||||
return personType + " " +
|
return personType + " " +
|
||||||
(!StringUtils.isEmpty(tradeName) ? tradeName : (familyName + ", " + givenName));
|
(!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")
|
|
||||||
.toRole("global", GUEST).grantPermission("person", INSERT)
|
|
||||||
|
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,24 +3,14 @@ package net.hostsharing.hsadminng.hs.office.relationship;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
import lombok.experimental.FieldNameConstants;
|
import lombok.experimental.FieldNameConstants;
|
||||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
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.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
|
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -77,52 +67,4 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
public String toShortString() {
|
public String toShortString() {
|
||||||
return toShortString.apply(this);
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,26 +6,16 @@ import lombok.*;
|
|||||||
import net.hostsharing.hsadminng.errors.DisplayName;
|
import net.hostsharing.hsadminng.errors.DisplayName;
|
||||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
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.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
|
|
||||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
|
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;
|
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@ -93,40 +83,4 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
|||||||
return reference;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RbacView rbac() {
|
|
||||||
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
|
|
||||||
.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", 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("debitor", AGENT);
|
|
||||||
})
|
|
||||||
.createSubRole(REFERRER, (with) -> {
|
|
||||||
with.incomingSuperRole("bankAccount", ADMIN);
|
|
||||||
with.incomingSuperRole("debitor", AGENT);
|
|
||||||
with.outgoingSubRole("debitorRel", TENANT);
|
|
||||||
with.permission(SELECT);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.hostsharing.hsadminng.persistence;
|
package net.hostsharing.hsadminng.persistence;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import java.util.UUID;
|
||||||
|
|
||||||
// TODO: remove this interface, I just wanted to avoid to many changes in that PR
|
public interface HasUuid {
|
||||||
public interface HasUuid extends RbacObject {
|
UUID getUuid();
|
||||||
}
|
}
|
||||||
|
@ -1,197 +0,0 @@
|
|||||||
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});
|
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
|
||||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
|
||||||
END LOOP;
|
|
||||||
END;
|
|
||||||
$$;
|
|
||||||
""",
|
|
||||||
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
|
||||||
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
|
||||||
with("rawSuperRoleDescriptor", toRoleDescriptor(superRoleDef, "row"))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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},
|
|
||||||
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", toRoleDescriptor(superRoleDef, PostgresTriggerReference.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 -> {
|
|
||||||
if (!g.getSuperRoleDef().getEntityAlias().isGlobal()) {
|
|
||||||
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()));
|
|
||||||
} else {
|
|
||||||
switch (g.getSuperRoleDef().getRole()) {
|
|
||||||
case ADMIN -> {
|
|
||||||
plPgSql.writeLn(
|
|
||||||
"""
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() -> {
|
|
||||||
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<RbacView.RbacGrantDefinition> getInsertGrants() {
|
|
||||||
return rbacDef.getGrantDefs().stream()
|
|
||||||
.filter(g -> g.grantType() == PERM_TO_ROLE)
|
|
||||||
.filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<RbacView.RbacGrantDefinition> getOptionalInsertGrant() {
|
|
||||||
return getInsertGrants()
|
|
||||||
.reduce(singleton());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<RbacView.RbacRoleDefinition> getOptionalInsertSuperRole() {
|
|
||||||
return getInsertGrants()
|
|
||||||
.map(RbacView.RbacGrantDefinition::getSuperRoleDef)
|
|
||||||
.reduce(singleton());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> BinaryOperator<T> 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String toRoleDescriptor(final RbacView.RbacRoleDefinition roleDef, final String ref) {
|
|
||||||
final var functionName = toVar(roleDef);
|
|
||||||
if (roleDef.getEntityAlias().isGlobal()) {
|
|
||||||
return functionName + "()";
|
|
||||||
}
|
|
||||||
return functionName + "(" + ref + ")";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
|
||||||
|
|
||||||
public enum PostgresTriggerReference {
|
|
||||||
NEW, OLD
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
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 generateRbacIdentityViewFromQuery('${rawTableName}', $idName$
|
|
||||||
${identityViewSqlPart}
|
|
||||||
$idName$);
|
|
||||||
""";
|
|
||||||
default -> throw new IllegalStateException("illegal SQL part given");
|
|
||||||
},
|
|
||||||
with("identityViewSqlPart", rbacDef.getIdentityViewSqlQuery().sql),
|
|
||||||
with("rawTableName", rawTableName));
|
|
||||||
|
|
||||||
plPgSql.writeLn("--//");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
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$
|
|
||||||
${orderBy}
|
|
||||||
$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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,840 +0,0 @@
|
|||||||
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.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 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<RbacUserReference> userDefs = new LinkedHashSet<>();
|
|
||||||
private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>();
|
|
||||||
private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>();
|
|
||||||
private final Map<String, EntityAlias> 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<String> updatableColumns = new LinkedHashSet<>();
|
|
||||||
private final Set<RbacGrantDefinition> grantDefs = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
private SQL identityViewSqlQuery;
|
|
||||||
private SQL orderBySqlExpression;
|
|
||||||
private EntityAlias rootEntityAliasProxy;
|
|
||||||
private RbacRoleDefinition previousRoleDef;
|
|
||||||
|
|
||||||
public static <E extends RbacObject> RbacView rbacViewFor(final String alias, final Class<E> 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<RbacRoleDefinition> 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<RbacRoleDefinition> 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 <EC extends RbacObject> RbacView declarePlaceholderEntityAliases(final String... aliasNames) {
|
|
||||||
for (String alias : aliasNames) {
|
|
||||||
entityAliases.put(alias, new EntityAlias(alias));
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <EC extends RbacObject> 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, NOT_NULL);
|
|
||||||
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, NOT_NULL);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RbacView importEntityAlias(
|
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
|
||||||
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
|
|
||||||
importEntityAliasImpl(aliasName, entityClass, fetchSql, dependsOnColum, false, nullable);
|
|
||||||
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, NOT_NULL);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RbacView importEntityAlias(
|
|
||||||
final String aliasName, final Class<? extends HasUuid> entityClass,
|
|
||||||
final Column dependsOnColum) {
|
|
||||||
importEntityAliasImpl(aliasName, entityClass, autoFetched(), 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 Nullable nullable) {
|
|
||||||
final var entityAlias = new EntityAlias(aliasName, entityClass, fetchSql, dependsOnColum, asSubEntity, nullable);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: switch order or parameters for more natural readability
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Nullable {
|
|
||||||
NOT_NULL, // DEFAULT
|
|
||||||
NULLABLE
|
|
||||||
}
|
|
||||||
|
|
||||||
@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, Nullable nullable) {
|
|
||||||
|
|
||||||
public EntityAlias(final String aliasName) {
|
|
||||||
this(aliasName, null, null, null, false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityAlias(final String aliasName, final Class<? extends RbacObject> entityClass) {
|
|
||||||
this(aliasName, entityClass, null, null, false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 enum Role {
|
|
||||||
|
|
||||||
OWNER,
|
|
||||||
ADMIN,
|
|
||||||
AGENT,
|
|
||||||
TENANT,
|
|
||||||
REFERRER,
|
|
||||||
|
|
||||||
GUEST;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return ":" + roleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
String roleName() {
|
|
||||||
return name().toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Permission {
|
|
||||||
INSERT,
|
|
||||||
DELETE,
|
|
||||||
UPDATE,
|
|
||||||
SELECT;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return ":" + name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<String> outerAliasNames;
|
|
||||||
|
|
||||||
AliasNameMapper(final RbacView importedRbacView, final String outerAliasName, final Set<String> 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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
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()
|
|
||||||
.replace("\n\n\n", "\n\n")
|
|
||||||
.replace("-- ====", "\n-- ====")
|
|
||||||
.replace("\n\n--//", "\n--//");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
public void generateToChangeLog(final Path outputPath) {
|
|
||||||
Files.writeString(
|
|
||||||
outputPath,
|
|
||||||
toString(),
|
|
||||||
StandardOpenOption.CREATE,
|
|
||||||
StandardOpenOption.TRUNCATE_EXISTING);
|
|
||||||
System.out.println(outputPath.toAbsolutePath());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,524 +0,0 @@
|
|||||||
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<RbacView.RbacGrantDefinition> 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);");
|
|
||||||
plPgSql.writeLn();
|
|
||||||
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(() -> {
|
|
||||||
referencedEntityAliases()
|
|
||||||
.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);");
|
|
||||||
plPgSql.writeLn();
|
|
||||||
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) -> {
|
|
||||||
generateFetchedVars(plPgSql, ea, NEW);
|
|
||||||
plPgSql.writeLn();
|
|
||||||
});
|
|
||||||
|
|
||||||
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<RbacView.EntityAlias> 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<RbacView.EntityAlias> updatableEntityAliases() {
|
|
||||||
return referencedEntityAliases()
|
|
||||||
.filter(ea -> rbacDef.getUpdatableColumns().contains(ea.dependsOnColum().column));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateUpdateRolesAndGrantsAfterUpdate(final StringWriter plPgSql) {
|
|
||||||
plPgSql.ensureSingleEmptyLine();
|
|
||||||
|
|
||||||
referencedEntityAliases()
|
|
||||||
.forEach((ea) -> {
|
|
||||||
generateFetchedVars(plPgSql, ea, OLD);
|
|
||||||
generateFetchedVars(plPgSql, ea, NEW);
|
|
||||||
plPgSql.writeLn();
|
|
||||||
});
|
|
||||||
|
|
||||||
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 void generateFetchedVars(
|
|
||||||
final StringWriter plPgSql,
|
|
||||||
final RbacView.EntityAlias ea,
|
|
||||||
final PostgresTriggerReference old) {
|
|
||||||
plPgSql.writeLn(
|
|
||||||
ea.fetchSql().sql + " INTO " + entityRefVar(old, ea) + ";",
|
|
||||||
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 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.name());
|
|
||||||
}
|
|
||||||
|
|
||||||
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::name)
|
|
||||||
.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<String> arrayElements, final int singleLineLimit) {
|
|
||||||
return arrayElements.size() <= singleLineLimit
|
|
||||||
? String.join(", ", arrayElements)
|
|
||||||
: arrayElements.stream().collect(joining(",\n\t", "\n\t", ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<RbacView.RbacGrantDefinition> 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<RbacView.RbacGrantDefinition> 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<RbacView.RbacGrantDefinition> 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<RbacView.RbacGrantDefinition> 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());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
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 (final RuntimeException exc) {
|
|
||||||
throw exc; // FIXME: just for debugging, remove try/catch before merging to master
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
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.
|
|
@ -103,7 +103,7 @@ public class RbacGrantController implements RbacGrantsApi {
|
|||||||
// public ResponseEntity<String> allGrantsOfUserAsMermaid(
|
// public ResponseEntity<String> allGrantsOfUserAsMermaid(
|
||||||
// @RequestHeader(name = "current-user") String currentUser,
|
// @RequestHeader(name = "current-user") String currentUser,
|
||||||
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
|
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
|
||||||
// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
|
// final var graph = RbacGrantsMermaidService.allGrantsToUser(currentUser);
|
||||||
// return ResponseEntity.ok(graph);
|
// return ResponseEntity.ok(graph);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -15,11 +15,11 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import static java.util.stream.Collectors.groupingBy;
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include.*;
|
import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include.*;
|
||||||
|
|
||||||
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
|
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
|
||||||
@Service
|
@Service
|
||||||
public class RbacGrantsDiagramService {
|
public class RbacGrantsMermaidService {
|
||||||
|
|
||||||
public static void writeToFile(final String title, final String graph, final String fileName) {
|
public static void writeToFile(final String title, final String graph, final String fileName) {
|
||||||
|
|
||||||
@ -68,13 +68,11 @@ public class RbacGrantsDiagramService {
|
|||||||
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
|
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm ")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( !g.getDescendantIdName().startsWith("role global")) {
|
if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
|
||||||
if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(" test_")) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
|
||||||
if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(" test_")) {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
graph.add(g);
|
graph.add(g);
|
||||||
if (includes.contains(NOT_ASSUMED) || g.isAssumed()) {
|
if (includes.contains(NOT_ASSUMED) || g.isAssumed()) {
|
||||||
@ -114,7 +112,7 @@ public class RbacGrantsDiagramService {
|
|||||||
new Node(g.getAscendantIdName(), g.getAscendingUuid()),
|
new Node(g.getAscendantIdName(), g.getAscendingUuid()),
|
||||||
new Node(g.getDescendantIdName(), g.getDescendantUuid()))
|
new Node(g.getDescendantIdName(), g.getDescendantUuid()))
|
||||||
)
|
)
|
||||||
.collect(groupingBy(RbacGrantsDiagramService::renderEntityIdName))
|
.collect(groupingBy(RbacGrantsMermaidService::renderEntityIdName))
|
||||||
.entrySet().stream()
|
.entrySet().stream()
|
||||||
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
.map(entity -> "subgraph " + quoted(entity.getKey()) + renderSubgraph(entity.getKey()) + "\n\n "
|
||||||
+ entity.getValue().stream()
|
+ entity.getValue().stream()
|
||||||
@ -127,9 +125,9 @@ public class RbacGrantsDiagramService {
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
final var grants = graph.stream()
|
final var grants = graph.stream()
|
||||||
.map(g -> quoted(g.getAscendantIdName())
|
.map(g -> quoted(g.getAscendantIdName()) +
|
||||||
+ " -->" + (g.isAssumed() ? " " : "|XX| ")
|
(g.isAssumed() ? " --> " : " -.-> ") +
|
||||||
+ quoted(g.getDescendantIdName()))
|
quoted(g.getDescendantIdName()))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(joining("\n"));
|
.collect(joining("\n"));
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacobject;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public interface RbacObject {
|
|
||||||
UUID getUuid();
|
|
||||||
}
|
|
@ -8,8 +8,8 @@ public interface RbacUserPermission {
|
|||||||
String getRoleName();
|
String getRoleName();
|
||||||
UUID getPermissionUuid();
|
UUID getPermissionUuid();
|
||||||
String getOp();
|
String getOp();
|
||||||
String getOpTableName();
|
|
||||||
String getObjectTable();
|
String getObjectTable();
|
||||||
String getObjectIdName();
|
String getObjectIdName();
|
||||||
UUID getObjectUuid();
|
UUID getObjectUuid();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
|
||||||
import jakarta.persistence.PersistenceContext;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -26,9 +24,6 @@ public class TestCustomerController implements TestCustomersApi {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TestCustomerRepository testCustomerRepository;
|
private TestCustomerRepository testCustomerRepository;
|
||||||
|
|
||||||
@PersistenceContext
|
|
||||||
EntityManager em;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public ResponseEntity<List<TestCustomerResource>> listCustomers(
|
public ResponseEntity<List<TestCustomerResource>> listCustomers(
|
||||||
@ -53,6 +48,7 @@ public class TestCustomerController implements TestCustomersApi {
|
|||||||
context.define(currentUser, assumedRoles);
|
context.define(currentUser, assumedRoles);
|
||||||
|
|
||||||
final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class));
|
final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class));
|
||||||
|
|
||||||
final var uri =
|
final var uri =
|
||||||
MvcUriComponentsBuilder.fromController(getClass())
|
MvcUriComponentsBuilder.fromController(getClass())
|
||||||
.path("/api/test/customers/{id}")
|
.path("/api/test/customers/{id}")
|
||||||
|
@ -4,27 +4,17 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
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 jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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
|
@Entity
|
||||||
@Table(name = "test_customer_rv")
|
@Table(name = "test_customer_rv")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class TestCustomerEntity implements HasUuid {
|
public class TestCustomerEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
@ -35,28 +25,4 @@ public class TestCustomerEntity implements HasUuid {
|
|||||||
|
|
||||||
@Column(name = "adminusername")
|
@Column(name = "adminusername")
|
||||||
private String 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")
|
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,28 +4,18 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
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 net.hostsharing.hsadminng.test.cust.TestCustomerEntity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
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
|
@Entity
|
||||||
@Table(name = "test_package_rv")
|
@Table(name = "test_package_rv")
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class TestPackageEntity implements HasUuid {
|
public class TestPackageEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
@ -41,34 +31,4 @@ public class TestPackageEntity implements HasUuid {
|
|||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private String description;
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,27 +23,22 @@ end; $$;
|
|||||||
Defines the transaction context.
|
Defines the transaction context.
|
||||||
*/
|
*/
|
||||||
create or replace procedure defineContext(
|
create or replace procedure defineContext(
|
||||||
currentTask varchar(96),
|
currentTask varchar,
|
||||||
currentRequest text = null,
|
currentRequest varchar = null,
|
||||||
currentUser varchar(63) = null,
|
currentUser varchar = null,
|
||||||
assumedRoles varchar(256) = null
|
assumedRoles varchar = null
|
||||||
)
|
)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
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);
|
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||||
|
|
||||||
currentRequest := coalesce(currentRequest, '');
|
currentRequest := coalesce(currentRequest, '');
|
||||||
execute format('set local hsadminng.currentRequest to %L', currentRequest);
|
execute format('set local hsadminng.currentRequest to %L', currentRequest);
|
||||||
|
|
||||||
currentUser := coalesce(currentUser, '');
|
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);
|
execute format('set local hsadminng.currentUser to %L', currentUser);
|
||||||
|
|
||||||
assumedRoles := coalesce(assumedRoles, '');
|
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);
|
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
|
||||||
|
|
||||||
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
|
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
|
||||||
|
@ -27,9 +27,9 @@ create table tx_context
|
|||||||
txId bigint not null,
|
txId bigint not null,
|
||||||
txTimestamp timestamp not null,
|
txTimestamp timestamp not null,
|
||||||
currentUser varchar(63) not null, -- not the uuid, because users can be deleted
|
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 not null, -- not the uuids, because roles can be deleted
|
||||||
currentTask varchar(96) not null,
|
currentTask varchar(96) not null,
|
||||||
currentRequest text not null
|
currentRequest varchar(512) not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create index on tx_context using brin (txTimestamp);
|
create index on tx_context using brin (txTimestamp);
|
||||||
|
@ -86,6 +86,29 @@ create or replace function findRbacUserId(userName varchar)
|
|||||||
language sql as $$
|
language sql as $$
|
||||||
select uuid from RbacUser where name = userName
|
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;
|
||||||
|
$$;
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
@ -180,33 +203,15 @@ create type RbacRoleDescriptor as
|
|||||||
(
|
(
|
||||||
objectTable varchar(63), -- for human readability and easier debugging
|
objectTable varchar(63), -- for human readability and easier debugging
|
||||||
objectUuid uuid,
|
objectUuid uuid,
|
||||||
roleType RbacRoleType,
|
roleType RbacRoleType
|
||||||
assumed boolean
|
|
||||||
);
|
);
|
||||||
|
|
||||||
create or replace function assumed()
|
create or replace function roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType)
|
||||||
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 RbacRoleDescriptor
|
||||||
returns null on null input
|
returns null on null input
|
||||||
stable -- leakproof
|
stable -- leakproof
|
||||||
language sql as $$
|
language sql as $$
|
||||||
select objectTable, objectUuid, roleType::RbacRoleType, assumed;
|
select objectTable, objectUuid, roleType::RbacRoleType;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace function createRole(roleDescriptor RbacRoleDescriptor)
|
create or replace function createRole(roleDescriptor RbacRoleDescriptor)
|
||||||
@ -270,17 +275,21 @@ create or replace function findRoleId(roleDescriptor RbacRoleDescriptor)
|
|||||||
select uuid from RbacRole where objectUuid = roleDescriptor.objectUuid and roleType = roleDescriptor.roleType;
|
select uuid from RbacRole where objectUuid = roleDescriptor.objectUuid and roleType = roleDescriptor.roleType;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace function getRoleId(roleDescriptor RbacRoleDescriptor)
|
create or replace function getRoleId(roleDescriptor RbacRoleDescriptor, whenNotExists RbacWhenNotExists)
|
||||||
returns uuid
|
returns uuid
|
||||||
|
returns null on null input
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
roleUuid uuid;
|
roleUuid uuid;
|
||||||
begin
|
begin
|
||||||
assert roleDescriptor is not null, 'roleDescriptor must not be null';
|
roleUuid = findRoleId(roleDescriptor);
|
||||||
|
|
||||||
roleUuid := findRoleId(roleDescriptor);
|
|
||||||
if (roleUuid is null) then
|
if (roleUuid is null) then
|
||||||
raise exception 'RbacRole "%#%.%" not found', roleDescriptor.objectTable, roleDescriptor.objectUuid, roleDescriptor.roleType;
|
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;
|
||||||
end if;
|
end if;
|
||||||
return roleUuid;
|
return roleUuid;
|
||||||
end;
|
end;
|
||||||
@ -356,65 +365,39 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
*/
|
*/
|
||||||
create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone
|
create domain RbacOp as varchar(67)
|
||||||
check (
|
check (
|
||||||
VALUE = 'DELETE'
|
VALUE = '*'
|
||||||
or VALUE = 'UPDATE'
|
or VALUE = 'delete'
|
||||||
or VALUE = 'SELECT'
|
or VALUE = 'edit'
|
||||||
or VALUE = 'INSERT'
|
or VALUE = 'view'
|
||||||
or VALUE = 'ASSUME'
|
or VALUE = 'assume'
|
||||||
-- TODO: all values below are deprecated, use insert with table
|
|
||||||
or VALUE ~ '^add-[a-z]+$'
|
or VALUE ~ '^add-[a-z]+$'
|
||||||
or VALUE ~ '^new-[a-z-]+$'
|
or VALUE ~ '^new-[a-z-]+$'
|
||||||
);
|
);
|
||||||
|
|
||||||
create table RbacPermission
|
create table RbacPermission
|
||||||
(
|
(
|
||||||
uuid uuid primary key references RbacReference (uuid) on delete cascade,
|
uuid uuid primary key references RbacReference (uuid) on delete cascade,
|
||||||
objectUuid uuid not null references RbacObject,
|
objectUuid uuid not null references RbacObject,
|
||||||
op RbacOp not null,
|
op RbacOp not null,
|
||||||
opTableName varchar(60)
|
unique (objectUuid, op)
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE RbacPermission
|
|
||||||
ADD CONSTRAINT RbacPermission_uc UNIQUE NULLS NOT DISTINCT (objectUuid, op, opTableName);
|
|
||||||
|
|
||||||
call create_journal('RbacPermission');
|
call create_journal('RbacPermission');
|
||||||
|
|
||||||
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
create or replace function permissionExists(forObjectUuid uuid, forOp RbacOp)
|
||||||
returns uuid
|
returns bool
|
||||||
language plpgsql as $$
|
language sql as $$
|
||||||
declare
|
select exists(
|
||||||
permissionUuid uuid;
|
select op
|
||||||
begin
|
from RbacPermission p
|
||||||
if (forObjectUuid is null) then
|
where p.objectUuid = forObjectUuid
|
||||||
raise exception 'forObjectUuid must not be null';
|
and p.op in ('*', forOp)
|
||||||
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);
|
-- TODO: the array parameter and thus the array return value is only used in toPermissionUuids, simplify to non-arrays
|
||||||
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[])
|
create or replace function createPermissions(forObjectUuid uuid, permitOps RbacOp[])
|
||||||
returns uuid[]
|
returns uuid[]
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
@ -425,6 +408,9 @@ begin
|
|||||||
if (forObjectUuid is null) then
|
if (forObjectUuid is null) then
|
||||||
raise exception 'forObjectUuid must not be null';
|
raise exception 'forObjectUuid must not be null';
|
||||||
end if;
|
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)
|
for i in array_lower(permitOps, 1)..array_upper(permitOps, 1)
|
||||||
loop
|
loop
|
||||||
@ -445,19 +431,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace function findEffectivePermissionId(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp)
|
||||||
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 uuid
|
||||||
returns null on null input
|
returns null on null input
|
||||||
stable -- leakproof
|
stable -- leakproof
|
||||||
@ -466,8 +440,22 @@ select uuid
|
|||||||
from RbacPermission p
|
from RbacPermission p
|
||||||
where p.objectUuid = forObjectUuid
|
where p.objectUuid = forObjectUuid
|
||||||
and p.op = forOp
|
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 $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
@ -583,18 +571,6 @@ 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)
|
create or replace function hasGlobalRoleGranted(userUuid uuid)
|
||||||
returns bool
|
returns bool
|
||||||
stable -- leakproof
|
stable -- leakproof
|
||||||
@ -609,27 +585,6 @@ select exists(
|
|||||||
);
|
);
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace procedure grantPermissionToRole(permissionUuid uuid, roleUuid 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(permissionUuid uuid, roleDesc RbacRoleDescriptor)
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
call grantPermissionToRole(permissionUuid, findRoleId(roleDesc));
|
|
||||||
end;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
-- TODO: deprecated, remove and use grantPermissionToRole(...)
|
|
||||||
create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[])
|
create or replace procedure grantPermissionsToRole(roleUuid uuid, permissionIds uuid[])
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
begin
|
begin
|
||||||
@ -671,11 +626,6 @@ declare
|
|||||||
superRoleId uuid;
|
superRoleId uuid;
|
||||||
subRoleId uuid;
|
subRoleId uuid;
|
||||||
begin
|
begin
|
||||||
-- FIXME: maybe separate method grantRoleToRoleIfNotNull(...)?
|
|
||||||
if superRole.objectUuid is null or subRole.objectuuid is null then
|
|
||||||
return;
|
|
||||||
end if;
|
|
||||||
|
|
||||||
superRoleId := findRoleId(superRole);
|
superRoleId := findRoleId(superRole);
|
||||||
subRoleId := findRoleId(subRole);
|
subRoleId := findRoleId(subRole);
|
||||||
|
|
||||||
@ -805,7 +755,7 @@ begin
|
|||||||
select descendantUuid
|
select descendantUuid
|
||||||
from grants) as granted
|
from grants) as granted
|
||||||
join RbacPermission perm
|
join RbacPermission perm
|
||||||
on granted.descendantUuid = perm.uuid and (requiredOp = 'SELECT' or perm.op = requiredOp)
|
on granted.descendantUuid = perm.uuid and perm.op in ('*', requiredOp)
|
||||||
join RbacObject obj on obj.uuid = perm.objectUuid and obj.objectTable = forObjectTable
|
join RbacObject obj on obj.uuid = perm.objectUuid and obj.objectTable = forObjectTable
|
||||||
limit maxObjects + 1;
|
limit maxObjects + 1;
|
||||||
|
|
||||||
@ -897,5 +847,6 @@ do $$
|
|||||||
create role restricted;
|
create role restricted;
|
||||||
grant all privileges on all tables in schema public to restricted;
|
grant all privileges on all tables in schema public to restricted;
|
||||||
end if;
|
end if;
|
||||||
end $$;
|
end $$
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -39,28 +39,17 @@ end; $$;
|
|||||||
|
|
||||||
create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)
|
create or replace procedure grantRoleToUser(grantedByRoleUuid uuid, grantedRoleUuid uuid, userUuid uuid, doAssume boolean = true)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
|
||||||
grantedByRoleIdName text;
|
|
||||||
grantedRoleIdName text;
|
|
||||||
begin
|
begin
|
||||||
perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
|
perform assertReferenceType('grantingRoleUuid', grantedByRoleUuid, 'RbacRole');
|
||||||
perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole');
|
perform assertReferenceType('grantedRoleUuid (descendant)', grantedRoleUuid, 'RbacRole');
|
||||||
perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser');
|
perform assertReferenceType('userUuid (ascendant)', userUuid, 'RbacUser');
|
||||||
|
|
||||||
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
|
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 %', grantedByRoleUuid, currentSubjects();
|
||||||
raise exception '[403] Access to granted-by-role % (%) forbidden for % (%)',
|
|
||||||
grantedByRoleIdName, grantedByRoleUuid, currentSubjects(), currentSubjectsUuids();
|
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then
|
if NOT isGranted(grantedByRoleUuid, grantedRoleUuid) then
|
||||||
select roleIdName from rbacRole_ev where uuid=grantedByRoleUuid into grantedByRoleIdName;
|
raise exception '[403] Access to granted role % forbidden for %', grantedRoleUuid, currentSubjects();
|
||||||
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;
|
end if;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
@ -112,17 +101,4 @@ begin
|
|||||||
where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid
|
where g.ascendantUuid = userUuid and g.descendantUuid = grantedRoleUuid
|
||||||
and g.grantedByRoleUuid = revokeRoleFromUser.grantedByRoleUuid;
|
and g.grantedByRoleUuid = revokeRoleFromUser.grantedByRoleUuid;
|
||||||
end; $$;
|
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; $$;
|
|
||||||
--//
|
|
||||||
|
@ -337,9 +337,11 @@ grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RES
|
|||||||
/*
|
/*
|
||||||
Returns all permissions granted to the given user,
|
Returns all permissions granted to the given user,
|
||||||
which are also visible to the current user or assumed roles.
|
which are also visible to the current user or assumed roles.
|
||||||
*/
|
|
||||||
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)
|
*/
|
||||||
|
create or replace function grantedPermissions(targetUserUuid uuid)
|
||||||
|
returns table(roleUuid uuid, roleName text, permissionUuid uuid, op RbacOp, objectTable varchar, objectIdName varchar, objectUuid uuid)
|
||||||
returns null on null input
|
returns null on null input
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
@ -355,13 +357,11 @@ begin
|
|||||||
return query select
|
return query select
|
||||||
xp.roleUuid,
|
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.permissionUuid, xp.op, xp.permissionObjectTable, xp.permissionObjectIdName, xp.permissionObjectUuid
|
||||||
xp.permissionObjectTable, xp.permissionObjectIdName, xp.permissionObjectUuid
|
|
||||||
from (select
|
from (select
|
||||||
r.uuid as roleUuid, r.roletype, ro.objectTable as roleObjectTable,
|
r.uuid as roleUuid, r.roletype, ro.objectTable as roleObjectTable,
|
||||||
findIdNameByObjectUuid(ro.objectTable, ro.uuid) as roleObjectIdName,
|
findIdNameByObjectUuid(ro.objectTable, ro.uuid) as roleObjectIdName,
|
||||||
p.uuid as permissionUuid, p.op, p.opTableName,
|
p.uuid as permissionUuid, p.op, po.objecttable as permissionObjectTable,
|
||||||
po.objecttable as permissionObjectTable,
|
|
||||||
findIdNameByObjectUuid(po.objectTable, po.uuid) as permissionObjectIdName,
|
findIdNameByObjectUuid(po.objectTable, po.uuid) as permissionObjectIdName,
|
||||||
po.uuid as permissionObjectUuid
|
po.uuid as permissionObjectUuid
|
||||||
from queryPermissionsGrantedToSubjectId( targetUserUuid) as p
|
from queryPermissionsGrantedToSubjectId( targetUserUuid) as p
|
||||||
@ -373,15 +373,4 @@ begin
|
|||||||
) xp;
|
) xp;
|
||||||
-- @formatter:on
|
-- @formatter:on
|
||||||
end; $$;
|
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;
|
|
||||||
$$;
|
|
||||||
--//
|
--//
|
||||||
|
@ -13,6 +13,24 @@ begin
|
|||||||
return createPermissions(forObjectUuid, permitOps);
|
return createPermissions(forObjectUuid, permitOps);
|
||||||
end; $$;
|
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
|
-- CREATE ROLE
|
||||||
@ -32,37 +50,32 @@ create or replace function createRoleWithGrants(
|
|||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
roleUuid uuid;
|
roleUuid uuid;
|
||||||
subRoleDesc RbacRoleDescriptor;
|
|
||||||
superRoleDesc RbacRoleDescriptor;
|
|
||||||
subRoleUuid uuid;
|
|
||||||
superRoleUuid uuid;
|
superRoleUuid uuid;
|
||||||
|
subRoleUuid uuid;
|
||||||
userUuid uuid;
|
userUuid uuid;
|
||||||
grantedByRoleUuid uuid;
|
grantedByRoleUuid uuid;
|
||||||
begin
|
begin
|
||||||
roleUuid := createRole(roleDescriptor);
|
roleUuid := createRole(roleDescriptor);
|
||||||
|
|
||||||
if cardinality(permissions) > 0 then
|
if cardinality(permissions) >0 then
|
||||||
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
|
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
foreach superRoleDesc in array array_remove(incomingSuperRoles, null)
|
foreach superRoleUuid in array toRoleUuids(incomingSuperRoles)
|
||||||
loop
|
loop
|
||||||
superRoleUuid := getRoleId(superRoleDesc);
|
call grantRoleToRole(roleUuid, superRoleUuid);
|
||||||
call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed);
|
|
||||||
end loop;
|
end loop;
|
||||||
|
|
||||||
foreach subRoleDesc in array array_remove(outgoingSubRoles, null)
|
foreach subRoleUuid in array toRoleUuids(outgoingSubRoles)
|
||||||
loop
|
loop
|
||||||
subRoleUuid := getRoleId(subRoleDesc);
|
call grantRoleToRole(subRoleUuid, roleUuid);
|
||||||
call grantRoleToRole(subRoleUuid, roleUuid, subRoleDesc.assumed);
|
|
||||||
end loop;
|
end loop;
|
||||||
|
|
||||||
if cardinality(userUuids) > 0 then
|
if cardinality(userUuids) > 0 then
|
||||||
if grantedByRole is null then
|
if grantedByRole is null then
|
||||||
grantedByRoleUuid := roleUuid;
|
raise exception 'to directly assign users to roles, grantingRole has to be given';
|
||||||
else
|
|
||||||
grantedByRoleUuid := getRoleId(grantedByRole);
|
|
||||||
end if;
|
end if;
|
||||||
|
grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
|
||||||
foreach userUuid in array userUuids
|
foreach userUuid in array userUuids
|
||||||
loop
|
loop
|
||||||
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
|
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
|
||||||
|
@ -13,7 +13,8 @@ declare
|
|||||||
begin
|
begin
|
||||||
createInsertTriggerSQL = format($sql$
|
createInsertTriggerSQL = format($sql$
|
||||||
create trigger createRbacObjectFor_%s_Trigger
|
create trigger createRbacObjectFor_%s_Trigger
|
||||||
before insert on %s
|
before insert
|
||||||
|
on %s
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertRelatedRbacObject();
|
execute procedure insertRelatedRbacObject();
|
||||||
$sql$, targetTable, targetTable);
|
$sql$, targetTable, targetTable);
|
||||||
@ -35,51 +36,51 @@ end; $$;
|
|||||||
--changeset rbac-generators-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
--changeset rbac-generators-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
create procedure generateRbacRoleDescriptors(prefix text, targetTable text)
|
create or replace procedure generateRbacRoleDescriptors(prefix text, targetTable text)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
sql text;
|
sql text;
|
||||||
begin
|
begin
|
||||||
sql = format($sql$
|
sql = format($sql$
|
||||||
create or replace function %1$sOwner(entity %2$s, assumed boolean = true)
|
create or replace function %1$sOwner(entity %2$s)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $f$
|
strict as $f$
|
||||||
begin
|
begin
|
||||||
return roleDescriptor('%2$s', entity.uuid, 'owner', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'owner');
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
create or replace function %1$sAdmin(entity %2$s, assumed boolean = true)
|
create or replace function %1$sAdmin(entity %2$s)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $f$
|
strict as $f$
|
||||||
begin
|
begin
|
||||||
return roleDescriptor('%2$s', entity.uuid, 'admin', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'admin');
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
create or replace function %1$sAgent(entity %2$s, assumed boolean = true)
|
create or replace function %1$sAgent(entity %2$s)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $f$
|
strict as $f$
|
||||||
begin
|
begin
|
||||||
return roleDescriptor('%2$s', entity.uuid, 'agent', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'agent');
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
create or replace function %1$sTenant(entity %2$s, assumed boolean = true)
|
create or replace function %1$sTenant(entity %2$s)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $f$
|
strict as $f$
|
||||||
begin
|
begin
|
||||||
return roleDescriptor('%2$s', entity.uuid, 'tenant', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'tenant');
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
-- TODO: remove guest role
|
-- TODO: remove guest role
|
||||||
create or replace function %1$sGuest(entity %2$s, assumed boolean = true)
|
create or replace function %1$sGuest(entity %2$s)
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $f$
|
strict as $f$
|
||||||
begin
|
begin
|
||||||
return roleDescriptor('%2$s', entity.uuid, 'guest', assumed);
|
return roleDescriptor('%2$s', entity.uuid, 'guest');
|
||||||
end; $f$;
|
end; $f$;
|
||||||
|
|
||||||
create or replace function %1$sReferrer(entity %2$s)
|
create or replace function %1$sReferrer(entity %2$s)
|
||||||
@ -100,7 +101,7 @@ end; $$;
|
|||||||
--changeset rbac-generators-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset rbac-generators-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
create or replace procedure generateRbacIdentityViewFromQuery(targetTable text, sqlQuery text)
|
create or replace procedure generateRbacIdentityView(targetTable text, idNameExpression text)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
sql text;
|
sql text;
|
||||||
@ -109,9 +110,11 @@ begin
|
|||||||
|
|
||||||
-- create a view to the target main table which maps an idName to the objectUuid
|
-- create a view to the target main table which maps an idName to the objectUuid
|
||||||
sql = format($sql$
|
sql = format($sql$
|
||||||
create or replace view %1$s_iv as %2$s;
|
create or replace view %1$s_iv as
|
||||||
|
select target.uuid, cleanIdentifier(%2$s) as idName
|
||||||
|
from %1$s as target;
|
||||||
grant all privileges on %1$s_iv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
grant all privileges on %1$s_iv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
||||||
$sql$, targetTable, sqlQuery);
|
$sql$, targetTable, idNameExpression);
|
||||||
execute sql;
|
execute sql;
|
||||||
|
|
||||||
-- creates a function which maps an idName to the objectUuid
|
-- creates a function which maps an idName to the objectUuid
|
||||||
@ -136,20 +139,6 @@ begin
|
|||||||
$sql$, targetTable);
|
$sql$, targetTable);
|
||||||
execute sql;
|
execute sql;
|
||||||
end; $$;
|
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; $$;
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
@ -165,13 +154,13 @@ begin
|
|||||||
targetTable := lower(targetTable);
|
targetTable := lower(targetTable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
Creates a restricted view based on the 'view' permission of the current subject.
|
||||||
*/
|
*/
|
||||||
sql := format($sql$
|
sql := format($sql$
|
||||||
set session session authorization default;
|
set session session authorization default;
|
||||||
create view %1$s_rv as
|
create view %1$s_rv as
|
||||||
with accessibleObjects as (
|
with accessibleObjects as (
|
||||||
select queryAccessibleObjectUuidsOfSubjectIds('SELECT', '%1$s', currentSubjectsUuids())
|
select queryAccessibleObjectUuidsOfSubjectIds('view', '%1$s', currentSubjectsUuids())
|
||||||
)
|
)
|
||||||
select target.*
|
select target.*
|
||||||
from %1$s as target
|
from %1$s as target
|
||||||
@ -220,7 +209,7 @@ begin
|
|||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $f$
|
language plpgsql as $f$
|
||||||
begin
|
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;
|
delete from %1$s p where p.uuid = old.uuid;
|
||||||
return old;
|
return old;
|
||||||
end if;
|
end if;
|
||||||
@ -243,7 +232,7 @@ begin
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Instead of update trigger function for the restricted view
|
Instead of update trigger function for the restricted view
|
||||||
based on the 'UPDATE' permission of the current subject.
|
based on the 'edit' permission of the current subject.
|
||||||
*/
|
*/
|
||||||
if columnUpdates is not null then
|
if columnUpdates is not null then
|
||||||
sql := format($sql$
|
sql := format($sql$
|
||||||
@ -251,7 +240,7 @@ begin
|
|||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $f$
|
language plpgsql as $f$
|
||||||
begin
|
begin
|
||||||
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('UPDATE', '%1$s', currentSubjectsUuids())) then
|
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('edit', '%1$s', currentSubjectsUuids())) then
|
||||||
update %1$s
|
update %1$s
|
||||||
set %2$s
|
set %2$s
|
||||||
where uuid = old.uuid;
|
where uuid = old.uuid;
|
||||||
|
@ -22,19 +22,6 @@ 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:--//
|
--changeset rbac-global-HAS-GLOBAL-PERMISSION:1 endDelimiter:--//
|
||||||
-- ------------------------------------------------------------------
|
-- ------------------------------------------------------------------
|
||||||
@ -109,41 +96,18 @@ commit;
|
|||||||
/*
|
/*
|
||||||
A global administrator role.
|
A global administrator role.
|
||||||
*/
|
*/
|
||||||
create or replace function globalAdmin(assumed boolean = true)
|
create or replace function globalAdmin()
|
||||||
returns RbacRoleDescriptor
|
returns RbacRoleDescriptor
|
||||||
returns null on null input
|
returns null on null input
|
||||||
stable -- leakproof
|
stable -- leakproof
|
||||||
language sql as $$
|
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;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
begin transaction;
|
begin transaction;
|
||||||
call defineContext('creating global admin role', null, null, null);
|
call defineContext('creating global admin role', null, null, null);
|
||||||
select createRole(globalAdmin());
|
select createRole(globalAdmin());
|
||||||
commit;
|
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:--//
|
--changeset rbac-global-ADMIN-USERS:1 context:dev,tc endDelimiter:--//
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
### 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
|
|
||||||
|
|
||||||
```
|
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.584886824.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -16,103 +15,82 @@ call generateRbacRoleDescriptors('testCustomer', 'test_customer');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-insert-trigger:1 endDelimiter:--//
|
--changeset test-customer-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
Creates the roles and their assignments for a new customer for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace procedure buildRbacSystemForTestCustomer(
|
create or replace function createRbacRolesForTestCustomer()
|
||||||
NEW test_customer
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
|
||||||
|
|
||||||
begin
|
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testCustomerOwner(NEW),
|
|
||||||
permissions => array['DELETE'],
|
|
||||||
userUuids => array[currentUserUuid()],
|
|
||||||
incomingSuperRoles => array[globalAdmin(unassumed())]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testCustomerAdmin(NEW),
|
|
||||||
permissions => array['UPDATE'],
|
|
||||||
incomingSuperRoles => array[testCustomerOwner(NEW)]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testCustomerTenant(NEW),
|
|
||||||
permissions => array['SELECT'],
|
|
||||||
incomingSuperRoles => array[testCustomerAdmin(NEW)]
|
|
||||||
);
|
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
AFTER INSERT TRIGGER to create the role+grant structure for a new test_customer row.
|
|
||||||
*/
|
|
||||||
|
|
||||||
create or replace function insertTriggerForTestCustomer_tf()
|
|
||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
|
declare
|
||||||
|
testCustomerOwnerUuid uuid;
|
||||||
|
customerAdminUuid uuid;
|
||||||
begin
|
begin
|
||||||
call buildRbacSystemForTestCustomer(NEW);
|
if TG_OP <> 'INSERT' then
|
||||||
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
|
-- the owner role with full access for Hostsharing administrators
|
||||||
|
testCustomerOwnerUuid = createRoleWithGrants(
|
||||||
|
testCustomerOwner(NEW),
|
||||||
|
permissions => array['*'],
|
||||||
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
|
);
|
||||||
|
|
||||||
|
-- the admin role for the customer's admins, who can view and add products
|
||||||
|
customerAdminUuid = 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()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 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']
|
||||||
|
);
|
||||||
|
|
||||||
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger insertTriggerForTestCustomer_tg
|
/*
|
||||||
after insert on test_customer
|
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||||
for each row
|
*/
|
||||||
execute procedure insertTriggerForTestCustomer_tf();
|
|
||||||
|
|
||||||
|
drop trigger if exists createRbacRolesForTestCustomer_Trigger on test_customer;
|
||||||
|
create trigger createRbacRolesForTestCustomer_Trigger
|
||||||
|
after insert
|
||||||
|
on test_customer
|
||||||
|
for each row
|
||||||
|
execute procedure createRbacRolesForTestCustomer();
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--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:--//
|
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
call generateRbacIdentityView('test_customer', $idName$
|
||||||
call generateRbacIdentityViewFromProjection('test_customer', $idName$
|
target.prefix
|
||||||
prefix
|
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_customer',
|
call generateRbacRestrictedView('test_customer', 'target.prefix',
|
||||||
'reference',
|
|
||||||
$updates$
|
$updates$
|
||||||
reference = new.reference,
|
reference = new.reference,
|
||||||
prefix = new.prefix,
|
prefix = new.prefix,
|
||||||
@ -121,3 +99,47 @@ call generateRbacRestrictedView('test_customer',
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--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();
|
||||||
|
--//
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@ declare
|
|||||||
currentTask varchar;
|
currentTask varchar;
|
||||||
custRowId uuid;
|
custRowId uuid;
|
||||||
custAdminName varchar;
|
custAdminName varchar;
|
||||||
custAdminUuid uuid;
|
|
||||||
newCust test_customer;
|
|
||||||
begin
|
begin
|
||||||
currentTask = 'creating RBAC test customer #' || custReference || '/' || custPrefix;
|
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');
|
||||||
@ -37,19 +35,10 @@ begin
|
|||||||
|
|
||||||
custRowId = uuid_generate_v4();
|
custRowId = uuid_generate_v4();
|
||||||
custAdminName = 'customer-admin@' || custPrefix || '.example.com';
|
custAdminName = 'customer-admin@' || custPrefix || '.example.com';
|
||||||
custAdminUuid = createRbacUser(custAdminName);
|
|
||||||
|
|
||||||
insert
|
insert
|
||||||
into test_customer (reference, prefix, adminUserName)
|
into test_customer (reference, prefix, adminUserName)
|
||||||
values (custReference, custPrefix, custAdminName);
|
values (custReference, custPrefix, custAdminName);
|
||||||
|
|
||||||
select * into newCust
|
|
||||||
from test_customer where reference=custReference;
|
|
||||||
call grantRoleToUser(
|
|
||||||
getRoleId(testCustomerOwner(newCust)),
|
|
||||||
getRoleId(testCustomerAdmin(newCust)),
|
|
||||||
custAdminUuid,
|
|
||||||
true);
|
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
### 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
|
|
||||||
|
|
||||||
```
|
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.625353859.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -16,211 +15,95 @@ call generateRbacRoleDescriptors('testPackage', 'test_package');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-insert-trigger:1 endDelimiter:--//
|
--changeset test-package-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
Creates the roles and their assignments for a new package for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
create or replace function createRbacRolesForTestPackage()
|
||||||
create or replace procedure buildRbacSystemForTestPackage(
|
|
||||||
NEW test_package
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
|
||||||
newCustomer test_customer;
|
|
||||||
|
|
||||||
begin
|
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
|
||||||
SELECT * FROM test_customer c
|
|
||||||
WHERE c.uuid= NEW.customerUuid
|
|
||||||
into newCustomer;
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testPackageOwner(NEW),
|
|
||||||
permissions => array['DELETE', 'UPDATE'],
|
|
||||||
incomingSuperRoles => array[testCustomerAdmin(newCustomer)]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testPackageAdmin(NEW),
|
|
||||||
incomingSuperRoles => array[testPackageOwner(NEW)]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
testPackageTenant(NEW),
|
|
||||||
permissions => array['SELECT'],
|
|
||||||
incomingSuperRoles => array[testPackageAdmin(NEW)],
|
|
||||||
outgoingSubRoles => array[testCustomerTenant(newCustomer)]
|
|
||||||
);
|
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
AFTER INSERT TRIGGER to create the role+grant structure for a new test_package row.
|
|
||||||
*/
|
|
||||||
|
|
||||||
create or replace function insertTriggerForTestPackage_tf()
|
|
||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
begin
|
|
||||||
call buildRbacSystemForTestPackage(NEW);
|
|
||||||
return NEW;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create trigger insertTriggerForTestPackage_tg
|
|
||||||
after insert on test_package
|
|
||||||
for each row
|
|
||||||
execute procedure insertTriggerForTestPackage_tf();
|
|
||||||
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset test-package-rbac-update-trigger:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
|
||||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
|
||||||
*/
|
|
||||||
|
|
||||||
create or replace procedure updateRbacRulesForTestPackage(
|
|
||||||
OLD test_package,
|
|
||||||
NEW test_package
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
declare
|
||||||
oldCustomer test_customer;
|
parentCustomer test_customer;
|
||||||
newCustomer test_customer;
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
if TG_OP <> 'INSERT' then
|
||||||
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
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;
|
end if;
|
||||||
|
|
||||||
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
|
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)]
|
||||||
|
);
|
||||||
|
|
||||||
|
-- an owner role is created and assigned to the package owner role
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
testPackageAdmin(NEW),
|
||||||
|
permissions => array ['add-domain'],
|
||||||
|
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)]
|
||||||
|
);
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
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;
|
return NEW;
|
||||||
end; $$;
|
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.
|
An AFTER INSERT TRIGGER which creates the role structure for a new package.
|
||||||
*/
|
*/
|
||||||
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
|
create trigger createRbacRolesForTestPackage_Trigger
|
||||||
LOOP
|
after insert
|
||||||
roleUuid := findRoleId(testCustomerAdmin(row));
|
on test_package
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
|
||||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
|
||||||
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(
|
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_package'),
|
|
||||||
testCustomerAdmin(NEW));
|
|
||||||
return NEW;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create trigger test_package_test_customer_insert_tg
|
|
||||||
after insert on test_customer
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_package_test_customer_insert_tf();
|
execute procedure createRbacRolesForTestPackage();
|
||||||
|
|
||||||
/**
|
|
||||||
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:--//
|
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
call generateRbacIdentityView('test_package', 'target.name');
|
||||||
call generateRbacIdentityViewFromProjection('test_package', $idName$
|
|
||||||
name
|
|
||||||
$idName$);
|
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_package',
|
|
||||||
'name',
|
/*
|
||||||
|
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_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$
|
$updates$
|
||||||
version = new.version,
|
version = new.version,
|
||||||
customerUuid = new.customerUuid,
|
customerUuid = new.customerUuid,
|
||||||
|
name = new.name,
|
||||||
description = new.description
|
description = new.description
|
||||||
$updates$);
|
$updates$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ begin
|
|||||||
|
|
||||||
custAdminUser = 'customer-admin@' || cust.prefix || '.example.com';
|
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);
|
call defineContext(currentTask, null, custAdminUser, custAdminRole);
|
||||||
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
|
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
@ -35,7 +35,7 @@ begin
|
|||||||
returning * into pac;
|
returning * into pac;
|
||||||
|
|
||||||
call grantRoleToUser(
|
call grantRoleToUser(
|
||||||
getRoleId(testCustomerAdmin(cust)),
|
getRoleId(testCustomerAdmin(cust), 'fail'),
|
||||||
findRoleId(testPackageAdmin(pac)),
|
findRoleId(testPackageAdmin(pac)),
|
||||||
createRbacUser('pac-admin-' || pacName || '@' || cust.prefix || '.example.com'),
|
createRbacUser('pac-admin-' || pacName || '@' || cust.prefix || '.example.com'),
|
||||||
true);
|
true);
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
### 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
|
|
||||||
|
|
||||||
```
|
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T11:29:11.645391647.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-domain-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -12,214 +11,107 @@ call generateRelatedRbacObject('test_domain');
|
|||||||
--changeset test-domain-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
--changeset test-domain-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRoleDescriptors('testDomain', 'test_domain');
|
call generateRbacRoleDescriptors('testDomain', 'test_domain');
|
||||||
--//
|
|
||||||
|
|
||||||
|
create or replace function createTestDomainTenantRoleIfNotExists(domain test_domain)
|
||||||
-- ============================================================================
|
returns uuid
|
||||||
--changeset test-domain-rbac-insert-trigger:1 endDelimiter:--//
|
returns null on null input
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
|
||||||
*/
|
|
||||||
|
|
||||||
create or replace procedure buildRbacSystemForTestDomain(
|
|
||||||
NEW test_domain
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
|
|
||||||
declare
|
declare
|
||||||
newPackage test_package;
|
domainTenantRoleDesc RbacRoleDescriptor;
|
||||||
|
domainTenantRoleUuid uuid;
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
domainTenantRoleDesc = testdomainTenant(domain);
|
||||||
SELECT * FROM test_package p
|
domainTenantRoleUuid = findRoleId(domainTenantRoleDesc);
|
||||||
WHERE p.uuid= NEW.packageUuid
|
if domainTenantRoleUuid is not null then
|
||||||
into newPackage;
|
return domainTenantRoleUuid;
|
||||||
|
|
||||||
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 $$
|
|
||||||
begin
|
|
||||||
call buildRbacSystemForTestDomain(NEW);
|
|
||||||
return NEW;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
create trigger insertTriggerForTestDomain_tg
|
|
||||||
after insert on test_domain
|
|
||||||
for each row
|
|
||||||
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;
|
end if;
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
return createRoleWithGrants(
|
||||||
|
domainTenantRoleDesc,
|
||||||
|
permissions => array['view'],
|
||||||
|
incomingSuperRoles => array[testdomainAdmin(domain)]
|
||||||
|
);
|
||||||
end; $$;
|
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:--//
|
--changeset test-domain-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates INSERT INTO test_domain permissions for the related test_package rows.
|
Creates the roles and their assignments for a new domain for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
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
|
create or replace function createRbacRulesForTestDomain()
|
||||||
LOOP
|
|
||||||
roleUuid := findRoleId(testPackageAdmin(row));
|
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_domain');
|
|
||||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
|
||||||
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
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
|
declare
|
||||||
|
parentPackage test_package;
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
if TG_OP <> 'INSERT' then
|
||||||
createPermission(NEW.uuid, 'INSERT', 'test_domain'),
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
testPackageAdmin(NEW));
|
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);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger test_domain_test_package_insert_tg
|
|
||||||
after insert on test_package
|
/*
|
||||||
|
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
|
||||||
for each row
|
for each row
|
||||||
execute procedure test_domain_test_package_insert_tf();
|
execute procedure createRbacRulesForTestDomain();
|
||||||
|
|
||||||
/**
|
|
||||||
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:--//
|
--changeset test-domain-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
call generateRbacIdentityView('test_domain', $idName$
|
||||||
call generateRbacIdentityViewFromProjection('test_domain', $idName$
|
target.name
|
||||||
name
|
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('test_domain',
|
|
||||||
'name',
|
/*
|
||||||
$updates$
|
Creates a view to the customer main table which maps the identifying name
|
||||||
version = new.version,
|
(in this case, the prefix) to the objectUuid.
|
||||||
packageUuid = new.packageUuid,
|
*/
|
||||||
description = new.description
|
drop view if exists test_domain_rv;
|
||||||
$updates$);
|
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};
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeContactOwner(NEW),
|
hsOfficeContactOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['*'],
|
||||||
incomingSuperRoles => array[globalAdmin()],
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
grantedByRole => globalAdmin()
|
grantedByRole => globalAdmin()
|
||||||
@ -41,13 +41,13 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeContactAdmin(NEW),
|
hsOfficeContactAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['edit'],
|
||||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeContactReferrer(NEW),
|
hsOfficeContactReferrer(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['view'],
|
||||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ execute procedure createRbacRolesForHsOfficeContact();
|
|||||||
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$
|
call generateRbacIdentityView('hs_office_contact', $idName$
|
||||||
target.label
|
target.label
|
||||||
$idName$);
|
$idName$);
|
||||||
--//
|
--//
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T15:13:04.479330676.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -16,134 +15,69 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-insert-trigger:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
create or replace function createRbacRolesForHsOfficePerson()
|
||||||
create or replace procedure buildRbacSystemForHsOfficePerson(
|
returns trigger
|
||||||
NEW hs_office_person
|
language plpgsql
|
||||||
)
|
strict as $$
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
if TG_OP <> 'INSERT' then
|
||||||
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
|
end if;
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonOwner(NEW),
|
hsOfficePersonOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['*'],
|
||||||
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
grantedByRole => globalAdmin()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to edit the data?
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonAdmin(NEW),
|
hsOfficePersonAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['edit'],
|
||||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonReferrer(NEW),
|
hsOfficePersonReferrer(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['view'],
|
||||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
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;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger insertTriggerForHsOfficePerson_tg
|
|
||||||
after insert on hs_office_person
|
|
||||||
for each row
|
|
||||||
execute procedure insertTriggerForHsOfficePerson_tf();
|
|
||||||
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-person-rbac-INSERT:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates INSERT INTO hs_office_person permissions for the related global rows.
|
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||||
*/
|
*/
|
||||||
do language plpgsql $$
|
|
||||||
declare
|
|
||||||
row global;
|
|
||||||
permissionUuid uuid;
|
|
||||||
roleUuid uuid;
|
|
||||||
begin
|
|
||||||
call defineContext('create INSERT INTO hs_office_person permissions for the related global rows');
|
|
||||||
|
|
||||||
FOR row IN SELECT * FROM global
|
create trigger createRbacRolesForHsOfficePerson_Trigger
|
||||||
LOOP
|
after insert
|
||||||
roleUuid := findRoleId(globalGuest());
|
on hs_office_person
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_person');
|
|
||||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
|
||||||
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; $$;
|
|
||||||
|
|
||||||
create trigger hs_office_person_global_insert_tg
|
|
||||||
after insert on global
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure hs_office_person_global_insert_tf();
|
execute procedure createRbacRolesForHsOfficePerson();
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_person.
|
|
||||||
*/
|
|
||||||
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; $$;
|
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--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)
|
||||||
concat(tradeName, familyName, givenName)
|
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('hs_office_person',
|
call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, target.familyName, target.givenName)',
|
||||||
'concat(tradeName, familyName, givenName)',
|
|
||||||
$updates$
|
$updates$
|
||||||
personType = new.personType,
|
personType = new.personType,
|
||||||
tradeName = new.tradeName,
|
tradeName = new.tradeName,
|
||||||
@ -153,3 +87,48 @@ call generateRbacRestrictedView('hs_office_person',
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--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();
|
||||||
|
--//
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ create or replace function hsOfficeRelationshipRbacRolesTrigger()
|
|||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
declare
|
declare
|
||||||
newAnchorPerson hs_office_person;
|
newAnchorPerson hs_office_person;
|
||||||
newHolderPerson hs_office_person;
|
newHolderPerson hs_office_person;
|
||||||
oldContact hs_office_contact;
|
oldContact hs_office_contact;
|
||||||
newContact hs_office_contact;
|
newContact hs_office_contact;
|
||||||
begin
|
begin
|
||||||
@ -44,7 +44,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipOwner(NEW),
|
hsOfficeRelationshipOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['*'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
globalAdmin()
|
globalAdmin()
|
||||||
]
|
]
|
||||||
@ -52,7 +52,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipAdmin(NEW),
|
hsOfficeRelationshipAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['edit'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeRelationshipOwner(NEW),
|
hsOfficeRelationshipOwner(NEW),
|
||||||
hsOfficePersonAdmin(newAnchorPerson)
|
hsOfficePersonAdmin(newAnchorPerson)
|
||||||
@ -70,7 +70,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipTenant(NEW),
|
hsOfficeRelationshipTenant(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['view'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeRelationshipAgent(NEW)
|
hsOfficeRelationshipAgent(NEW)
|
||||||
],
|
],
|
||||||
@ -130,7 +130,7 @@ execute procedure hsOfficeRelationshipRbacRolesTrigger();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
|
call generateRbacIdentityView('hs_office_relationship', $idName$
|
||||||
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
||||||
|| '-with-' || target.relType || '-' ||
|
|| '-with-' || target.relType || '-' ||
|
||||||
(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.relHolderUuid)
|
||||||
@ -142,8 +142,8 @@ call generateRbacIdentityViewFromProjection('hs_office_relationship', $idName$
|
|||||||
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('hs_office_relationship',
|
call generateRbacRestrictedView('hs_office_relationship',
|
||||||
'(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.relHolderUuid)',
|
||||||
$updates$
|
$updates$
|
||||||
contactUuid = new.contactUuid
|
contactUuid = new.contactUuid
|
||||||
$updates$);
|
$updates$);
|
||||||
--//
|
--//
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
create or replace procedure createHsOfficeRelationshipTestData(
|
create or replace procedure createHsOfficeRelationshipTestData(
|
||||||
holderPersonName varchar,
|
holderPersonName varchar,
|
||||||
relationshipType HsOfficeRelationshipType,
|
relationshipType HsOfficeRelationshipType,
|
||||||
anchorPersonName varchar,
|
anchorPersonTradeName varchar,
|
||||||
contactLabel varchar,
|
contactLabel varchar,
|
||||||
mark varchar default null)
|
mark varchar default null)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
@ -23,28 +23,24 @@ declare
|
|||||||
contact hs_office_contact;
|
contact hs_office_contact;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
idName := cleanIdentifier( anchorPersonName || '-' || holderPersonName);
|
idName := cleanIdentifier( anchorPersonTradeName || '-' || holderPersonName);
|
||||||
currentTask := 'creating relationship test-data ' || idName;
|
currentTask := 'creating relationship 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);
|
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||||
|
|
||||||
select p.*
|
select p.* from hs_office_person p where p.tradeName = anchorPersonTradeName into anchorPerson;
|
||||||
into anchorPerson
|
|
||||||
from hs_office_person p
|
|
||||||
where p.tradeName = anchorPersonName or p.familyName = anchorPersonName;
|
|
||||||
if anchorPerson is null then
|
if anchorPerson is null then
|
||||||
raise exception 'anchorPerson "%" not found', anchorPersonName;
|
raise exception 'anchorPerson "%" not found', anchorPersonTradeName;
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
select p.*
|
select p.* from hs_office_person p
|
||||||
into holderPerson
|
where p.tradeName = holderPersonName or p.familyName = holderPersonName
|
||||||
from hs_office_person p
|
into holderPerson;
|
||||||
where p.tradeName = holderPersonName or p.familyName = holderPersonName;
|
|
||||||
if holderPerson is null then
|
if holderPerson is null then
|
||||||
raise exception 'holderPerson "%" not found', holderPersonName;
|
raise exception 'holderPerson "%" not found', holderPersonName;
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
select c.* into contact from hs_office_contact c where c.label = contactLabel;
|
select c.* from hs_office_contact c where c.label = contactLabel into contact;
|
||||||
if contact is null then
|
if contact is null then
|
||||||
raise exception 'contact "%" not found', contactLabel;
|
raise exception 'contact "%" not found', contactLabel;
|
||||||
end if;
|
end if;
|
||||||
@ -91,22 +87,17 @@ do language plpgsql $$
|
|||||||
begin
|
begin
|
||||||
call createHsOfficeRelationshipTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
|
call createHsOfficeRelationshipTestData('First GmbH', 'PARTNER', 'Hostsharing eG', 'first contact');
|
||||||
call createHsOfficeRelationshipTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
|
call createHsOfficeRelationshipTestData('Firby', 'REPRESENTATIVE', 'First GmbH', 'first contact');
|
||||||
call createHsOfficeRelationshipTestData('First GmbH', 'ACCOUNTING', 'First GmbH', 'first contact');
|
|
||||||
|
|
||||||
call createHsOfficeRelationshipTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
|
call createHsOfficeRelationshipTestData('Second e.K.', 'PARTNER', 'Hostsharing eG', 'second contact');
|
||||||
call createHsOfficeRelationshipTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
|
call createHsOfficeRelationshipTestData('Smith', 'REPRESENTATIVE', 'Second e.K.', 'second contact');
|
||||||
call createHsOfficeRelationshipTestData('Second e.K.', 'ACCOUNTING', 'Second e.K.', 'second contact');
|
|
||||||
|
|
||||||
call createHsOfficeRelationshipTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
|
call createHsOfficeRelationshipTestData('Third OHG', 'PARTNER', 'Hostsharing eG', 'third contact');
|
||||||
call createHsOfficeRelationshipTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
call createHsOfficeRelationshipTestData('Tucker', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
||||||
call createHsOfficeRelationshipTestData('Third OHG', 'ACCOUNTING', 'Third OHG', 'third contact');
|
|
||||||
|
|
||||||
call createHsOfficeRelationshipTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
|
call createHsOfficeRelationshipTestData('Fourth eG', 'PARTNER', 'Hostsharing eG', 'fourth contact');
|
||||||
call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
|
||||||
call createHsOfficeRelationshipTestData('Third OHG', 'ACCOUNTING', 'Third OHG', 'third contact');
|
|
||||||
|
|
||||||
call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
|
call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
|
||||||
call createHsOfficeRelationshipTestData('Smith', 'ACCOUNTING', 'Smith', 'third contact');
|
|
||||||
call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
|
call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
@ -1,158 +1,72 @@
|
|||||||
### rbac partner
|
### hs_office_partner RBAC
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T15:29:41.494727519.
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
|
||||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
subgraph external[ ]
|
||||||
direction TB
|
style external fill:#fff
|
||||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
subgraph global
|
||||||
subgraph partnerRel.contact:roles[ ]
|
style global fill:#eee
|
||||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
|
||||||
|
role:global.admin[global.admin]
|
||||||
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:new-partner{{partner:new-partner}}
|
|
||||||
perm:partner:DELETE{{partner:DELETE}}
|
|
||||||
perm:partner:UPDATE{{partner:UPDATE}}
|
|
||||||
perm:partner:SELECT{{partner:SELECT}}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph partnerRel["`**partnerRel**`"]
|
subgraph partnerPerson
|
||||||
|
style partnerPerson fill:#eee
|
||||||
|
|
||||||
|
role:partnerPerson.admin[partnerPerson.admin]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph otherRelatedPerson
|
||||||
|
style otherRelatedPerson fill:#eee
|
||||||
|
|
||||||
|
role:otherRelatedPerson.admin[otherRelatedPerson.admin]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph hsOfficeRelationship[hsOfficeRelationship:PARTNER]
|
||||||
direction TB
|
direction TB
|
||||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style hsOfficeRelationship fill:#eee
|
||||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
|
||||||
direction TB
|
|
||||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
|
||||||
subgraph partnerRel.contact:roles[ ]
|
role:global.admin
|
||||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
--> role:hsOfficeRelationship.owner[relationship.owner]
|
||||||
|
--> role:hsOfficeRelationship.admin[relationship.admin]
|
||||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
--> role:hsOfficeRelationship.agent[relationship.agent]
|
||||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
--> role:hsOfficeRelationship.tenant[relationship.tenant]
|
||||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
|
||||||
end
|
role:partnerPerson.admin --> role:hsOfficeRelationship.agent
|
||||||
end
|
role:otherRelatedPerson.admin --> role:hsOfficeRelationship.tenant
|
||||||
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph partnerDetails["`**partnerDetails**`"]
|
subgraph internal[ ]
|
||||||
direction TB
|
|
||||||
style partnerDetails fill:#feb28c,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
|
||||||
subgraph partnerDetails:permissions[ ]
|
subgraph hsOfficePartner
|
||||||
style partnerDetails:permissions fill:#feb28c,stroke:white
|
style hsOfficePartner fill:#fff
|
||||||
|
|
||||||
perm:partnerDetails:DELETE{{partnerDetails:DELETE}}
|
perm:hsOfficePartner.*{{partner.*}}
|
||||||
perm:partnerDetails:UPDATE{{partnerDetails:UPDATE}}
|
role:hsOfficeRelationship.owner ==> perm:hsOfficePartner.*
|
||||||
perm:partnerDetails:SELECT{{partnerDetails:SELECT}}
|
|
||||||
|
perm:hsOfficePartner.edit{{partner.edit}}
|
||||||
|
role:hsOfficeRelationship.admin ==> perm:hsOfficePartner.edit
|
||||||
|
|
||||||
|
perm:hsOfficePartner.view{{partner.view}}
|
||||||
|
role:hsOfficeRelationship.tenant ==> perm:hsOfficePartner.view
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
subgraph partnerRel.anchorPerson["`**partnerRel.anchorPerson**`"]
|
subgraph hsOfficePartnerDetails
|
||||||
direction TB
|
direction TB
|
||||||
style partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style hsOfficePartnerDetails fill:#eee
|
||||||
|
|
||||||
|
perm:hsOfficePartnerDetails.*{{partnerDetails.*}}
|
||||||
|
role:hsOfficeRelationship.owner ==> perm:hsOfficePartnerDetails.*
|
||||||
|
|
||||||
subgraph partnerRel.anchorPerson:roles[ ]
|
perm:hsOfficePartnerDetails.edit{{partnerDetails.edit}}
|
||||||
style partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.edit
|
||||||
|
role:hsOfficeRelationship.agent ==> perm:hsOfficePartnerDetails.view
|
||||||
role:partnerRel.anchorPerson:owner[[partnerRel.anchorPerson:owner]]
|
|
||||||
role:partnerRel.anchorPerson:admin[[partnerRel.anchorPerson:admin]]
|
perm:hsOfficePartnerDetails.view{{partnerDetails.view}}
|
||||||
role:partnerRel.anchorPerson:referrer[[partnerRel.anchorPerson:referrer]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
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:new-partner
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -35,39 +35,39 @@ begin
|
|||||||
-- Permissions and Grants for Partner
|
-- Permissions and Grants for Partner
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerUuid, array ['DELETE'])
|
createPermissions(partnerUuid, array ['*'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerUuid, array ['UPDATE'])
|
createPermissions(partnerUuid, array ['edit'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerUuid, array ['SELECT'])
|
createPermissions(partnerUuid, array ['view'])
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Permissions and Grants for PartnerDetails
|
-- Permissions and Grants for PartnerDetails
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerDetailsUuid, array ['DELETE'])
|
createPermissions(partnerDetailsUuid, array ['*'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerDetailsUuid, array ['UPDATE'])
|
createPermissions(partnerDetailsUuid, array ['edit'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT.
|
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficeRelationshipTENANT.
|
||||||
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
|
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
|
||||||
-- Otherwise package-admins etc. would be able to read the data.
|
-- Otherwise package-admins etc. would be able to read the data.
|
||||||
getRoleId(hsOfficeRelationshipAgent(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'),
|
||||||
createPermissions(partnerDetailsUuid, array ['SELECT'])
|
createPermissions(partnerDetailsUuid, array ['view'])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
elsif TG_OP = 'UPDATE' then
|
elsif TG_OP = 'UPDATE' then
|
||||||
@ -78,72 +78,72 @@ begin
|
|||||||
-- Revokes from Partner
|
-- Revokes from Partner
|
||||||
|
|
||||||
call revokePermissionFromRole(
|
call revokePermissionFromRole(
|
||||||
findPermissionId(partnerUuid, 'SELECT'),
|
findPermissionId(partnerUuid, 'view'),
|
||||||
hsOfficeRelationshipTenant(oldPartnerRel)
|
hsOfficeRelationshipTenant(oldPartnerRel)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- call revokePermissionFromRole(
|
-- call revokePermissionFromRole(
|
||||||
-- findPermissionId(partnerUuid, 'edit'),
|
-- findPermissionId(partnerUuid, 'edit'),
|
||||||
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
||||||
-- );
|
-- );
|
||||||
--
|
--
|
||||||
-- call revokePermissionFromRole(
|
-- call revokePermissionFromRole(
|
||||||
-- findPermissionId(partnerUuid, '*'),
|
-- findPermissionId(partnerUuid, '*'),
|
||||||
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
||||||
-- );
|
-- );
|
||||||
|
|
||||||
-- Grants for Partner
|
-- Grants for Partner
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerUuid, 'DELETE')]
|
array[findPermissionId(partnerUuid, '*')]
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerUuid, 'UPDATE')]
|
array[findPermissionId(partnerUuid, 'edit')]
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipTenant(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipTenant(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerUuid, 'SELECT')]
|
array[findPermissionId(partnerUuid, 'view')]
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Revokes from PartnerDetails
|
-- Revokes from PartnerDetails
|
||||||
|
|
||||||
-- call revokePermissionFromRole(
|
-- call revokePermissionFromRole(
|
||||||
-- findPermissionId(partnerDetailsUuid, 'SELECT'),
|
-- findPermissionId(partnerDetailsUuid, 'view'),
|
||||||
-- hsOfficeRelationshipAgent(oldPartnerRel)
|
-- hsOfficeRelationshipAgent(oldPartnerRel)
|
||||||
-- );
|
-- );
|
||||||
--
|
--
|
||||||
-- call revokePermissionFromRole(
|
-- call revokePermissionFromRole(
|
||||||
-- findPermissionId(partnerDetailsUuid, 'UPDATE'),
|
-- findPermissionId(partnerDetailsUuid, 'edit'),
|
||||||
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
-- hsOfficeRelationshipAdmin(oldPartnerRel)
|
||||||
-- );
|
-- );
|
||||||
--
|
--
|
||||||
-- call revokePermissionFromRole(
|
-- call revokePermissionFromRole(
|
||||||
-- findPermissionId(partnerDetailsUuid, 'DELETE'),
|
-- findPermissionId(partnerDetailsUuid, '*'),
|
||||||
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
-- hsOfficeRelationshipOwner(oldPartnerRel)
|
||||||
-- );
|
-- );
|
||||||
|
|
||||||
-- Grants for PartnerDetails
|
-- Grants for PartnerDetails
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipOwner(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipOwner(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerDetailsUuid, 'DELETE')]
|
array[findPermissionId(partnerDetailsUuid, '*')]
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAdmin(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerDetailsUuid, 'UPDATE')]
|
array[findPermissionId(partnerDetailsUuid, 'edit')]
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
|
-- Yes, here hsOfficePartnerAGENT is used, not hsOfficePartnerTENANT.
|
||||||
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
|
-- Do NOT grant view permission on partner-details to hsOfficeRelationshipTENANT!
|
||||||
-- Otherwise package-admins etc. would be able to read the data.
|
-- Otherwise package-admins etc. would be able to read the data.
|
||||||
getRoleId(hsOfficeRelationshipAgent(newPartnerRel)),
|
getRoleId(hsOfficeRelationshipAgent(newPartnerRel), 'fail'),
|
||||||
array[findPermissionId(partnerDetailsUuid, 'SELECT')]
|
array[findPermissionId(partnerDetailsUuid, 'view')]
|
||||||
);
|
);
|
||||||
|
|
||||||
end if;
|
end if;
|
||||||
@ -179,7 +179,7 @@ execute procedure hsOfficePartnerRbacRolesTrigger();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_partner', $idName$
|
call generateRbacIdentityView('hs_office_partner', $idName$
|
||||||
partnerNumber || ':' ||
|
partnerNumber || ':' ||
|
||||||
(select idName
|
(select idName
|
||||||
from hs_office_person_iv p
|
from hs_office_person_iv p
|
||||||
|
@ -7,10 +7,13 @@ call generateRelatedRbacObject('hs_office_partner_details');
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-partner-details-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_partner_details', $idName$
|
call generateRbacIdentityView('hs_office_partner_details', $idName$
|
||||||
(select idName || '-details' from hs_office_partner_iv partner_iv
|
(select idName || '-details' from hs_office_partner_iv partner_iv
|
||||||
join hs_office_partner partner on (partner_iv.uuid = partner.uuid)
|
join hs_office_partner partner on (partner_iv.uuid = partner.uuid)
|
||||||
where partner.detailsUuid = target.uuid)
|
where partner.detailsUuid = target.uuid)
|
||||||
@ -35,7 +38,7 @@ call generateRbacRestrictedView('hs_office_partner_details',
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-partner-details-rbac-NEW-PARTNER-DETAILS:1 endDelimiter:--//
|
--changeset hs-office-partner-details-rbac-NEW-CONTACT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
|
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
|
||||||
|
@ -1,45 +1,40 @@
|
|||||||
### rbac bankAccount
|
### hs_office_bankaccount RBAC Roles
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T19:09:38.350576842.
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
|
||||||
subgraph bankAccount["`**bankAccount**`"]
|
subgraph global
|
||||||
direction TB
|
style hsOfficeBankAccount fill: #e9f7ef
|
||||||
style bankAccount fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
|
||||||
subgraph bankAccount:roles[ ]
|
role:global.admin[global.admin]
|
||||||
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
|
end
|
||||||
|
|
||||||
%% granting roles to users
|
subgraph hsOfficeBankAccount
|
||||||
user:creator ==> role:bankAccount:owner
|
direction TB
|
||||||
|
style hsOfficeBankAccount fill: #e9f7ef
|
||||||
%% granting roles to roles
|
|
||||||
role:global:admin ==> role:bankAccount:owner
|
user:hsOfficeBankAccount.creator([bankAccount.creator])
|
||||||
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: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
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T19:09:38.359318650.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
|
--changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -16,141 +15,125 @@ call generateRbacRoleDescriptors('hsOfficeBankAccount', 'hs_office_bankaccount')
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-bankaccount-rbac-insert-trigger:1 endDelimiter:--//
|
--changeset hs-office-bankaccount-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
Creates the roles and their assignments for a new bankaccount for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace procedure buildRbacSystemForHsOfficeBankAccount(
|
create or replace function createRbacRolesForHsOfficeBankAccount()
|
||||||
NEW hs_office_bankaccount
|
returns trigger
|
||||||
)
|
language plpgsql
|
||||||
language plpgsql as $$
|
strict as $$
|
||||||
|
|
||||||
declare
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
if TG_OP <> 'INSERT' then
|
||||||
|
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||||
|
end if;
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeBankAccountOwner(NEW),
|
hsOfficeBankAccountOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['delete'],
|
||||||
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
grantedByRole => globalAdmin()
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeBankAccountAdmin(NEW),
|
hsOfficeBankAccountAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
|
||||||
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeBankAccountReferrer(NEW),
|
hsOfficeBankAccountTenant(NEW),
|
||||||
permissions => array['SELECT'],
|
|
||||||
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
|
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
perform createRoleWithGrants(
|
||||||
end; $$;
|
hsOfficeBankAccountGuest(NEW),
|
||||||
|
permissions => array['view'],
|
||||||
|
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger insertTriggerForHsOfficeBankAccount_tg
|
|
||||||
after insert on hs_office_bankaccount
|
|
||||||
for each row
|
|
||||||
execute procedure insertTriggerForHsOfficeBankAccount_tf();
|
|
||||||
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates INSERT INTO hs_office_bankaccount permissions for the related global rows.
|
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||||
*/
|
*/
|
||||||
do language plpgsql $$
|
|
||||||
declare
|
|
||||||
row global;
|
|
||||||
permissionUuid uuid;
|
|
||||||
roleUuid uuid;
|
|
||||||
begin
|
|
||||||
call defineContext('create INSERT INTO hs_office_bankaccount permissions for the related global rows');
|
|
||||||
|
|
||||||
FOR row IN SELECT * FROM global
|
create trigger createRbacRolesForHsOfficeBankAccount_Trigger
|
||||||
LOOP
|
after insert
|
||||||
roleUuid := findRoleId(globalGuest());
|
on hs_office_bankaccount
|
||||||
permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_bankaccount');
|
|
||||||
call grantPermissionToRole(permissionUuid, roleUuid);
|
|
||||||
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; $$;
|
|
||||||
|
|
||||||
create trigger hs_office_bankaccount_global_insert_tg
|
|
||||||
after insert on global
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure hs_office_bankaccount_global_insert_tf();
|
execute procedure createRbacRolesForHsOfficeBankAccount();
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if the user or assumed roles are allowed to insert a row to hs_office_bankaccount.
|
|
||||||
*/
|
|
||||||
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; $$;
|
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$
|
call generateRbacIdentityView('hs_office_bankaccount', $idName$
|
||||||
concat(iban, ':', holder)
|
target.holder
|
||||||
$idName$);
|
$idName$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('hs_office_bankaccount',
|
call generateRbacRestrictedView('hs_office_bankaccount', 'target.holder',
|
||||||
$orderBy$
|
|
||||||
concat(iban, ':', holder)
|
|
||||||
$orderBy$,
|
|
||||||
$updates$
|
$updates$
|
||||||
holder = new.holder,
|
holder = new.holder,
|
||||||
iban = new.iban,
|
iban = new.iban,
|
||||||
bic = new.bic
|
bic = new.bic
|
||||||
$updates$);
|
$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();
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,178 +1,71 @@
|
|||||||
### rbac sepaMandate
|
### hs_office_sepaMandate RBAC
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T18:29:47.084556363.
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
|
||||||
subgraph bankAccount["`**bankAccount**`"]
|
subgraph global
|
||||||
direction TB
|
style global fill:#eee
|
||||||
style bankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
role:global.admin[global.admin]
|
||||||
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
|
end
|
||||||
|
|
||||||
subgraph debitorRel.contact["`**debitorRel.contact**`"]
|
subgraph hsOfficeBankAccount
|
||||||
direction TB
|
direction TB
|
||||||
style debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style hsOfficeBankAccount fill:#eee
|
||||||
|
|
||||||
subgraph debitorRel.contact:roles[ ]
|
role:hsOfficeBankAccount.owner[bankAccount.owner]
|
||||||
style debitorRel.contact:roles fill:#99bcdb,stroke:white
|
--> role:hsOfficeBankAccount.admin[bankAccount.admin]
|
||||||
|
--> role:hsOfficeBankAccount.tenant[bankAccount.tenant]
|
||||||
role:debitorRel.contact:owner[[debitorRel.contact:owner]]
|
--> role:hsOfficeBankAccount.guest[bankAccount.guest]
|
||||||
role:debitorRel.contact:admin[[debitorRel.contact:admin]]
|
|
||||||
role:debitorRel.contact:referrer[[debitorRel.contact:referrer]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
subgraph hsOfficeDebitor
|
||||||
direction TB
|
direction TB
|
||||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style hsOfficeDebitor fill:#eee
|
||||||
|
|
||||||
subgraph debitorRel.anchorPerson:roles[ ]
|
role:hsOfficeDebitor.owner[debitor.admin]
|
||||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
--> role:hsOfficeDebitor.admin[debitor.admin]
|
||||||
|
--> role:hsOfficeDebitor.agent[debitor.agent]
|
||||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
--> role:hsOfficeDebitor.tenant[debitor.tenant]
|
||||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
--> role:hsOfficeDebitor.guest[debitor.guest]
|
||||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
subgraph hsOfficeSepaMandate
|
||||||
direction TB
|
|
||||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
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.holderPerson:roles[ ]
|
role:hsOfficeSepaMandate.guest[sepaMandate.guest]
|
||||||
style debitorRel.holderPerson:roles fill:#99bcdb,stroke:white
|
%% permissions
|
||||||
|
role:hsOfficeSepaMandate.guest --> perm:hsOfficeSepaMandate.view{{sepaMandate.view}}
|
||||||
role:debitorRel.holderPerson:owner[[debitorRel.holderPerson:owner]]
|
%% incoming
|
||||||
role:debitorRel.holderPerson:admin[[debitorRel.holderPerson:admin]]
|
role:hsOfficeSepaMandate.tenant --> role:hsOfficeSepaMandate.guest
|
||||||
role:debitorRel.holderPerson:referrer[[debitorRel.holderPerson:referrer]]
|
|
||||||
end
|
|
||||||
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
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-11T18:29:47.095199204.
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
|
--changeset hs-office-sepamandate-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -16,123 +15,95 @@ call generateRbacRoleDescriptors('hsOfficeSepaMandate', 'hs_office_sepamandate')
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-sepamandate-rbac-insert-trigger:1 endDelimiter:--//
|
--changeset hs-office-sepamandate-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
Creates and updates the roles and their assignments for sepaMandate entities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace procedure buildRbacSystemForHsOfficeSepaMandate(
|
create or replace function hsOfficeSepaMandateRbacRolesTrigger()
|
||||||
NEW hs_office_sepamandate
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
|
||||||
newBankAccount hs_office_bankaccount;
|
|
||||||
newDebitorRel hs_office_relationship;
|
|
||||||
|
|
||||||
begin
|
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
|
||||||
SELECT * FROM hs_office_bankaccount WHERE uuid = NEW.bankAccountUuid into newBankAccount;
|
|
||||||
SELECT * FROM hs_office_relationship WHERE uuid = NEW.debitorUuid into newDebitorRel;
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
hsOfficeSepaMandateOwner(NEW),
|
|
||||||
permissions => array['DELETE'],
|
|
||||||
userUuids => array[currentUserUuid()],
|
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
hsOfficeSepaMandateAdmin(NEW),
|
|
||||||
permissions => array['UPDATE'],
|
|
||||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
hsOfficeSepaMandateAgent(NEW),
|
|
||||||
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW)],
|
|
||||||
outgoingSubRoles => array[
|
|
||||||
hsOfficeBankAccountReferrer(newBankAccount),
|
|
||||||
hsOfficeRelationshipAgent(newDebitorRel)]
|
|
||||||
);
|
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
|
||||||
hsOfficeSepaMandateReferrer(NEW),
|
|
||||||
permissions => array['SELECT'],
|
|
||||||
incomingSuperRoles => array[
|
|
||||||
hsOfficeRelationshipAgent(newDebitorRel),
|
|
||||||
hsOfficeBankAccountAdmin(newBankAccount),
|
|
||||||
hsOfficeSepaMandateAgent(NEW)],
|
|
||||||
outgoingSubRoles => array[hsOfficeRelationshipTenant(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
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
|
declare
|
||||||
|
newHsOfficeDebitor hs_office_debitor;
|
||||||
|
newHsOfficeBankAccount hs_office_bankAccount;
|
||||||
begin
|
begin
|
||||||
call buildRbacSystemForHsOfficeSepaMandate(NEW);
|
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;
|
||||||
|
|
||||||
|
if TG_OP = 'INSERT' then
|
||||||
|
|
||||||
|
-- === ATTENTION: code generated from related Mermaid flowchart: ===
|
||||||
|
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
hsOfficeSepaMandateOwner(NEW),
|
||||||
|
permissions => array['*'],
|
||||||
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
|
);
|
||||||
|
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
hsOfficeSepaMandateAdmin(NEW),
|
||||||
|
permissions => array['edit'],
|
||||||
|
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
|
||||||
|
outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
|
||||||
|
);
|
||||||
|
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
hsOfficeSepaMandateAgent(NEW),
|
||||||
|
incomingSuperRoles => array[hsOfficeSepaMandateAdmin(NEW), hsOfficeDebitorAdmin(newHsOfficeDebitor), hsOfficeBankAccountAdmin(newHsOfficeBankAccount)],
|
||||||
|
outgoingSubRoles => array[hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||||
|
);
|
||||||
|
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
hsOfficeSepaMandateTenant(NEW),
|
||||||
|
incomingSuperRoles => array[hsOfficeSepaMandateAgent(NEW)],
|
||||||
|
outgoingSubRoles => array[hsOfficeDebitorGuest(newHsOfficeDebitor), hsOfficeBankAccountGuest(newHsOfficeBankAccount)]
|
||||||
|
);
|
||||||
|
|
||||||
|
perform createRoleWithGrants(
|
||||||
|
hsOfficeSepaMandateGuest(NEW),
|
||||||
|
permissions => array['view'],
|
||||||
|
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
|
||||||
|
);
|
||||||
|
|
||||||
|
-- === END of code generated from Mermaid flowchart. ===
|
||||||
|
|
||||||
|
else
|
||||||
|
raise exception 'invalid usage of TRIGGER';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger insertTriggerForHsOfficeSepaMandate_tg
|
/*
|
||||||
after insert on hs_office_sepamandate
|
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||||
|
*/
|
||||||
|
create trigger createRbacRolesForHsOfficeSepaMandate_Trigger
|
||||||
|
after insert
|
||||||
|
on hs_office_sepamandate
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerForHsOfficeSepaMandate_tf();
|
execute procedure hsOfficeSepaMandateRbacRolesTrigger();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--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.
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
-- 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 hs_office_sepamandate_insert_permission_missing_tf();
|
|
||||||
|
|
||||||
--//
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-sepamandate-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
call generateRbacIdentityView('hs_office_sepamandate', idNameExpression => 'target.reference');
|
||||||
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:--//
|
--changeset hs-office-sepamandate-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('hs_office_sepamandate',
|
call generateRbacRestrictedView('hs_office_sepamandate',
|
||||||
'validity',
|
orderby => 'target.reference',
|
||||||
$updates$
|
columnUpdates => $updates$
|
||||||
reference = new.reference,
|
reference = new.reference,
|
||||||
agreement = new.agreement,
|
agreement = new.agreement,
|
||||||
validity = new.validity
|
validity = new.validity
|
||||||
@ -140,3 +111,48 @@ call generateRbacRestrictedView('hs_office_sepamandate',
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--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();
|
||||||
|
--//
|
||||||
|
|
||||||
|
@ -23,14 +23,12 @@ begin
|
|||||||
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);
|
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||||
|
|
||||||
select debitor.* into relatedDebitor
|
select debitor.*
|
||||||
from hs_office_debitor debitor
|
from hs_office_debitor debitor
|
||||||
join hs_office_relationship debitorRel on debitorRel.uuid = debitor.debitorRelUuid
|
left join hs_office_partner partner on debitor.partneruuid = partner.uuid
|
||||||
join hs_office_relationship partnerRel on partnerRel.relHolderUuid = debitorRel.relAnchorUuid
|
where partner.partnerNumber = forPartnerNumber and debitor.debitorNumberSuffix = forDebitorSuffix
|
||||||
join hs_office_partner partner on partner.partnerRoleUuid = partnerRel.uuid
|
into relatedDebitor;
|
||||||
where partner.partnerNumber = forPartnerNumber and debitor.debitorNumberSuffix = forDebitorSuffix;
|
select b.* from hs_office_bankAccount b where b.iban = forIban into relatedBankAccount;
|
||||||
select b.* into relatedBankAccount
|
|
||||||
from hs_office_bankAccount b where b.iban = forIban;
|
|
||||||
|
|
||||||
raise notice 'creating test SEPA-mandate: %', forPartnerNumber::text || forDebitorSuffix::text;
|
raise notice 'creating test SEPA-mandate: %', forPartnerNumber::text || forDebitorSuffix::text;
|
||||||
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
|
||||||
|
@ -1,275 +1,74 @@
|
|||||||
### rbac debitor
|
### hs_office_debitor RBAC Roles
|
||||||
|
|
||||||
This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-12T16:22:27.339854728.
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
|
||||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
subgraph partnerRelationship[hsOfficeRelationship:PARTNER]
|
||||||
direction TB
|
direction TB
|
||||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style partnerRelationship fill:#eee
|
||||||
|
|
||||||
|
role:partnerRelationship.owner[relationship.owner]
|
||||||
|
--> role:partnerRelationship.admin[relationship.admin]
|
||||||
|
--> role:partnerRelationship.agent[relationship.agent]
|
||||||
|
--> role:partnerRelationship.tenant[relationship.tenant]
|
||||||
|
|
||||||
subgraph debitorRel.anchorPerson:roles[ ]
|
partnerPersonAdmin>e.g. partnerPerson.admin] --> role:partnerRelationship.agent
|
||||||
style debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white
|
otherPersonAdmin>e.g. operationalPerson.admin] --> role:partnerRelationship.tenant
|
||||||
|
role:partnerRelationship.tenant --> partnerPersonReferrer>e.g. partnerPerson.referrer]
|
||||||
role:debitorRel.anchorPerson:owner[[debitorRel.anchorPerson:owner]]
|
|
||||||
role:debitorRel.anchorPerson:admin[[debitorRel.anchorPerson:admin]]
|
|
||||||
role:debitorRel.anchorPerson:referrer[[debitorRel.anchorPerson:referrer]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph debitorRel.holderPerson["`**debitorRel.holderPerson**`"]
|
subgraph internal[ ]
|
||||||
direction TB
|
direction TB
|
||||||
style debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style internal fill:#fff
|
||||||
|
|
||||||
subgraph debitorRel.holderPerson:roles[ ]
|
subgraph refundBankAccount
|
||||||
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
|
direction TB
|
||||||
style debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style refundBankAccount fill:#eee
|
||||||
subgraph debitorRel.anchorPerson["`**debitorRel.anchorPerson**`"]
|
|
||||||
direction TB
|
role:refundBankAccount.owner[bankAccount.owner]
|
||||||
style debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
--> role:refundBankAccount.admin[bankAccount.admin]
|
||||||
|
--> role:refundBankAccount.referrer[bankAccount.referrer]
|
||||||
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
|
||||||
end
|
|
||||||
|
|
||||||
subgraph partnerRel["`**partnerRel**`"]
|
|
||||||
direction TB
|
|
||||||
style partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
|
||||||
subgraph partnerRel.holderPerson["`**partnerRel.holderPerson**`"]
|
subgraph debitorRelationship[hsOfficeRelationship:DEBITOR]
|
||||||
direction TB
|
direction TB
|
||||||
style partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
style debitorRelationship fill:#eee
|
||||||
|
|
||||||
subgraph partnerRel.holderPerson:roles[ ]
|
role:debitorRelationship.owner[relationship.owner]
|
||||||
style partnerRel.holderPerson:roles fill:#99bcdb,stroke:white
|
--> role:debitorRelationship.admin[relationship.admin]
|
||||||
|
--> role:debitorRelationship.agent[relationship.agent]
|
||||||
role:partnerRel.holderPerson:owner[[partnerRel.holderPerson:owner]]
|
--> role:debitorRelationship.tenant[relationship.tenant]
|
||||||
role:partnerRel.holderPerson:admin[[partnerRel.holderPerson:admin]]
|
|
||||||
role:partnerRel.holderPerson:referrer[[partnerRel.holderPerson:referrer]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph partnerRel.contact["`**partnerRel.contact**`"]
|
subgraph debitor
|
||||||
direction TB
|
direction TB
|
||||||
style partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px
|
|
||||||
|
role:debitorRelationship.owner[debitorRelationship.owner]
|
||||||
|
%% permissions
|
||||||
|
==> perm:debitor.*{{debitor.*}}
|
||||||
|
|
||||||
subgraph partnerRel.contact:roles[ ]
|
role:debitorRelationship.admin[debitorRelationship.admin]
|
||||||
style partnerRel.contact:roles fill:#99bcdb,stroke:white
|
%% permissions
|
||||||
|
==> perm:debitor.edit{{debitor.edit}}
|
||||||
|
%% incoming
|
||||||
|
role:partnerRelationship.admin ==> role:debitorRelationship.admin
|
||||||
|
%% outgoing
|
||||||
|
role:debitorRelationship.admin ==> role:partnerRelationship.agent
|
||||||
|
|
||||||
role:partnerRel.contact:owner[[partnerRel.contact:owner]]
|
role:debitorRelationship.agent[debitorRelationship.agent]
|
||||||
role:partnerRel.contact:admin[[partnerRel.contact:admin]]
|
%% incoming
|
||||||
role:partnerRel.contact:referrer[[partnerRel.contact:referrer]]
|
role:partnerRelationship.agent ==> role:debitorRelationship.agent
|
||||||
end
|
role:refundBankAccount.admin ==> role:debitorRelationship.agent
|
||||||
|
%% outgoing
|
||||||
|
role:debitorRelationship.agent ==> role:partnerRelationship.tenant
|
||||||
|
role:debitorRelationship.agent ==> role:refundBankAccount.referrer
|
||||||
|
|
||||||
|
role:debitorRelationship.tenant[debitorRelationship.tenant]
|
||||||
|
==> perm:debitor.view{{debitor.view}}
|
||||||
|
|
||||||
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
|
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
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
-- This code generated was by RbacViewPostgresGenerator at 2024-03-12T16:22:27.348469700.
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
|
--changeset hs-office-debitor-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -10,280 +8,248 @@ call generateRelatedRbacObject('hs_office_debitor');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-debitor-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
--changeset hs-office-debitor-rbac-ROLES-CREATION: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.
|
Creates and updates the roles and their assignments for debitor entities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace procedure buildRbacSystemForHsOfficeDebitor(
|
create or replace function hsOfficeDebitorRbacRolesTrigger()
|
||||||
NEW hs_office_debitor
|
|
||||||
)
|
|
||||||
language plpgsql as $$
|
|
||||||
|
|
||||||
declare
|
|
||||||
newPartnerRel hs_office_relationship;
|
|
||||||
newDebitorRel hs_office_relationship;
|
|
||||||
newRefundBankAccount hs_office_bankaccount;
|
|
||||||
|
|
||||||
begin
|
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
|
||||||
|
|
||||||
SELECT partnerRel.*
|
|
||||||
FROM hs_office_relationship AS partnerRel
|
|
||||||
JOIN hs_office_relationship AS debitorRel
|
|
||||||
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
|
|
||||||
WHERE partnerRel.relType = '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_relationship AS r
|
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.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_relationship AS r
|
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
|
|
||||||
INTO newRefundBankAccount;
|
|
||||||
|
|
||||||
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
|
|
||||||
call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel));
|
|
||||||
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
|
|
||||||
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel));
|
|
||||||
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel));
|
|
||||||
|
|
||||||
call grantPermissionToRole(createPermission(NEW.uuid, 'DELETE'), hsOfficeRelationshipOwner(newDebitorRel));
|
|
||||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), hsOfficeRelationshipTenant(newDebitorRel));
|
|
||||||
call grantPermissionToRole(createPermission(NEW.uuid, 'UPDATE'), hsOfficeRelationshipAdmin(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
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
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 $$
|
|
||||||
|
|
||||||
declare
|
declare
|
||||||
oldPartnerRel hs_office_relationship;
|
debitorUuid uuid;
|
||||||
newPartnerRel hs_office_relationship;
|
|
||||||
oldDebitorRel hs_office_relationship;
|
oldDebitorRel hs_office_relationship;
|
||||||
newDebitorRel hs_office_relationship;
|
newDebitorRel hs_office_relationship;
|
||||||
oldRefundBankAccount hs_office_bankaccount;
|
|
||||||
newRefundBankAccount hs_office_bankaccount;
|
newPartnerRel hs_office_relationship;
|
||||||
|
|
||||||
|
newBankAccount hs_office_bankaccount;
|
||||||
|
oldBankAccount hs_office_bankaccount;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
SELECT partnerRel.*
|
debitorUuid := NEW.uuid;
|
||||||
FROM hs_office_relationship AS partnerRel
|
|
||||||
JOIN hs_office_relationship AS debitorRel
|
|
||||||
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
|
|
||||||
WHERE partnerRel.relType = 'PARTNER'
|
|
||||||
AND OLD.debitorRelUuid = debitorRel.uuid
|
|
||||||
INTO oldPartnerRel;
|
|
||||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
|
|
||||||
|
|
||||||
SELECT partnerRel.*
|
select * into newDebitorRel
|
||||||
FROM hs_office_relationship AS partnerRel
|
from hs_office_relationship as r where r.relType = 'DEBITOR' and r.relHolderUuid = NEW.debitorRelUuid;
|
||||||
JOIN hs_office_relationship AS debitorRel
|
|
||||||
ON debitorRel.relType = 'ACCOUNTING' AND debitorRel.relAnchorUuid = partnerRel.relHolderUuid
|
|
||||||
WHERE partnerRel.relType = '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 *
|
select * into newPartnerRel
|
||||||
FROM hs_office_relationship AS r
|
from hs_office_relationship as r
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.uuid = OLD.debitorRelUuid
|
join hs_office_partner as p on p.partnerRoleUuid = r.uuid
|
||||||
INTO oldDebitorRel;
|
where r.relType = 'PARTNER' and r.relHolderUuid = newPartnerRel;
|
||||||
assert oldDebitorRel.uuid is not null, format('oldDebitorRel must not be null for OLD.debitorRelUuid = %s', OLD.debitorRelUuid);
|
|
||||||
|
|
||||||
SELECT *
|
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid
|
||||||
FROM hs_office_relationship AS r
|
into newBankAccount;
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.uuid = NEW.debitorRelUuid
|
|
||||||
INTO newDebitorRel;
|
|
||||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
|
||||||
|
|
||||||
SELECT *
|
if TG_OP = 'INSERT' then
|
||||||
FROM hs_office_relationship AS r
|
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = OLD.debitorRelUuid
|
|
||||||
INTO oldRefundBankAccount;
|
|
||||||
SELECT *
|
|
||||||
FROM hs_office_relationship AS r
|
|
||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = NEW.debitorRelUuid
|
|
||||||
INTO newRefundBankAccount;
|
|
||||||
|
|
||||||
if NEW.refundBankAccountUuid <> OLD.refundBankAccountUuid then
|
-- Permissions and Grants for Debitor
|
||||||
|
|
||||||
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldRefundBankAccount));
|
call grantPermissionsToRole(
|
||||||
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newRefundBankAccount));
|
getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['*'])
|
||||||
|
);
|
||||||
|
|
||||||
call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldRefundBankAccount), hsOfficeRelationshipAgent(oldDebitorRel));
|
call grantPermissionsToRole(
|
||||||
call grantRoleToRole(hsOfficeBankAccountReferrer(newRefundBankAccount), hsOfficeRelationshipAgent(newDebitorRel));
|
getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['edit'])
|
||||||
|
);
|
||||||
|
|
||||||
|
call grantPermissionsToRole(
|
||||||
|
getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['view'])
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Grants to and from related Partner Relationship
|
||||||
|
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true);
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true);
|
||||||
|
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel), true);
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel), true);
|
||||||
|
|
||||||
|
-- Grants to and from refundBankAccount
|
||||||
|
|
||||||
|
if newBankAccount is not null then
|
||||||
|
call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
elsif TG_OP = 'UPDATE' then
|
||||||
|
|
||||||
|
if OLD.debitorRelUuid is distinct from NEW.debitorRelUuid then
|
||||||
|
|
||||||
|
select * into oldDebitorRel
|
||||||
|
from hs_office_relationship as r where r.relType = 'DEBITOR' and r.relHolderUuid = NEW.debitorRelUuid;
|
||||||
|
|
||||||
|
call grantPermissionsToRole(
|
||||||
|
getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['*'])
|
||||||
|
);
|
||||||
|
|
||||||
|
call grantPermissionsToRole(
|
||||||
|
getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['edit'])
|
||||||
|
);
|
||||||
|
|
||||||
|
call grantPermissionsToRole(
|
||||||
|
getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
|
||||||
|
createPermissions(partnerUuid, array ['view'])
|
||||||
|
);
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if OLD.refundBankAccountUuid is distinct from NEW.refundBankAccountUuid then
|
||||||
|
|
||||||
|
select * into oldBankAccount
|
||||||
|
from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid;
|
||||||
|
|
||||||
|
if oldBankAccount is not null then
|
||||||
|
call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldBankAccount), hsOfficeRelationshipAgent(oldDebitorRel), true);
|
||||||
|
call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldBankAccount), true);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if newBankAccount is not null then
|
||||||
|
call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
|
||||||
|
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
else
|
||||||
|
raise exception 'invalid usage of TRIGGER';
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
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;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger updateTriggerForHsOfficeDebitor_tg
|
/*
|
||||||
after update on hs_office_debitor
|
An AFTER INSERT TRIGGER which creates the role structure for a new debitor.
|
||||||
|
*/
|
||||||
|
create trigger createRbacRolesForHsOfficeDebitor_Trigger
|
||||||
|
after insert
|
||||||
|
on hs_office_debitor
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerForHsOfficeDebitor_tf();
|
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
|
||||||
|
for each row
|
||||||
|
execute procedure hsOfficeDebitorRbacRolesTrigger();
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-debitor-rbac-INSERT:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates INSERT INTO hs_office_debitor permissions for the related global rows.
|
Creates and updates the roles and their assignments for debitor entities if partner rel changes.
|
||||||
*/
|
*/
|
||||||
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
|
create or replace function hsOfficeDebitorPartnerRelRbacRolesTrigger()
|
||||||
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
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
|
declare
|
||||||
|
|
||||||
begin
|
begin
|
||||||
call grantPermissionToRole(
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
globalAdmin(),
|
|
||||||
createPermission(NEW.uuid, 'INSERT', 'hs_office_debitor'));
|
-- TODO
|
||||||
|
|
||||||
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger 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.
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
/*
|
||||||
|
An AFTER UPDATE TRIGGER which creates the role structure for debitors if partner relations change.
|
||||||
|
*/
|
||||||
|
create trigger updateRbacRolesForHsOfficeDebitor_Trigger
|
||||||
|
after update
|
||||||
|
on hs_office_partner
|
||||||
|
for each row
|
||||||
|
execute procedure hsOfficeDebitorPartnerRelRbacRolesTrigger();
|
||||||
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
call generateRbacIdentityView('hs_office_debitor', $idName$
|
||||||
call generateRbacIdentityViewFromQuery('hs_office_debitor', $idName$
|
'#' ||
|
||||||
SELECT debitor.uuid AS uuid,
|
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
|
||||||
'D-' || (SELECT partner.partnerNumber
|
to_char(debitorNumberSuffix, 'fm00') ||
|
||||||
FROM hs_office_partner partner
|
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid)
|
||||||
JOIN hs_office_relationship partnerRel
|
$idName$);
|
||||||
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') as idName
|
|
||||||
FROM hs_office_debitor AS debitor
|
|
||||||
|
|
||||||
$idName$);
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacRestrictedView('hs_office_debitor',
|
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix',
|
||||||
$orderBy$
|
|
||||||
defaultPrefix
|
|
||||||
$orderBy$,
|
|
||||||
$updates$
|
$updates$
|
||||||
debitorRel = new.debitorRel,
|
partnerUuid = new.partnerUuid, -- TODO: remove? should never do anything
|
||||||
billable = new.billable,
|
billable = new.billable,
|
||||||
debitorUuid = new.debitorUuid,
|
billingContactUuid = new.billingContactUuid,
|
||||||
|
debitorNumberSuffix = new.debitorNumberSuffix, -- TODO: Should it be allowed to updated this value?
|
||||||
refundBankAccountUuid = new.refundBankAccountUuid,
|
refundBankAccountUuid = new.refundBankAccountUuid,
|
||||||
vatId = new.vatId,
|
vatId = new.vatId,
|
||||||
vatCountryCode = new.vatCountryCode,
|
vatCountryCode = new.vatCountryCode,
|
||||||
vatBusiness = new.vatBusiness,
|
vatBusiness = new.vatBusiness,
|
||||||
vatReverseCharge = new.vatReverseCharge,
|
vatreversecharge = new.vatreversecharge,
|
||||||
defaultPrefix = new.defaultPrefix
|
defaultPrefix = new.defaultPrefix -- TODO: Should it be allowed to updated this value?
|
||||||
$updates$);
|
$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();
|
||||||
|
--//
|
||||||
|
|
||||||
|
@ -9,41 +9,37 @@
|
|||||||
Creates a single debitor test record.
|
Creates a single debitor test record.
|
||||||
*/
|
*/
|
||||||
create or replace procedure createHsOfficeDebitorTestData(
|
create or replace procedure createHsOfficeDebitorTestData(
|
||||||
withDebitorNumberSuffix numeric(5),
|
debitorNumberSuffix numeric(5),
|
||||||
forPartnerPersonName varchar,
|
partnerTradeName varchar,
|
||||||
forBillingContactLabel varchar,
|
billingContactLabel varchar,
|
||||||
withDefaultPrefix varchar
|
defaultPrefix varchar
|
||||||
)
|
)
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
currentTask varchar;
|
currentTask varchar;
|
||||||
idName varchar;
|
idName varchar;
|
||||||
relatedDebitorRelUuid uuid;
|
relatedPartner hs_office_partner;
|
||||||
|
relatedContact hs_office_contact;
|
||||||
relatedBankAccountUuid uuid;
|
relatedBankAccountUuid uuid;
|
||||||
begin
|
begin
|
||||||
idName := cleanIdentifier( forPartnerPersonName|| '-' || forBillingContactLabel);
|
idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel);
|
||||||
currentTask := 'creating debitor test-data ' || idName;
|
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);
|
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||||
|
|
||||||
select debitorRel.uuid
|
select partner.* from hs_office_partner partner
|
||||||
into relatedDebitorRelUuid
|
join hs_office_relationship rel on rel.uuid = partner.partnerRoleUuid
|
||||||
from hs_office_relationship debitorRel
|
join hs_office_person person on person.uuid = rel.relHolderUuid
|
||||||
join hs_office_person person on person.uuid = debitorRel.relHolderUuid
|
where person.tradeName = partnerTradeName into relatedPartner;
|
||||||
and (person.tradeName = forPartnerPersonName or person.familyName = forPartnerPersonName)
|
select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact;
|
||||||
where debitorRel.relType = 'ACCOUNTING';
|
select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid;
|
||||||
|
|
||||||
select b.uuid
|
raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
|
||||||
into relatedBankAccountUuid
|
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
|
||||||
from hs_office_bankaccount b
|
raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;
|
||||||
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
|
insert
|
||||||
into hs_office_debitor (uuid, debitorRelUuid, debitornumbersuffix, billable, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
|
into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix)
|
||||||
values (uuid_generate_v4(), relatedDebitorRelUuid, withDebitorNumberSuffix, true, true, false, relatedBankAccountUuid, withDefaultPrefix);
|
values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, false, relatedBankAccountUuid, defaultPrefix);
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -43,13 +43,13 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipOwner(NEW),
|
hsOfficeMembershipOwner(NEW),
|
||||||
permissions => array['DELETE'],
|
permissions => array['*'],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipAdmin(NEW),
|
hsOfficeMembershipAdmin(NEW),
|
||||||
permissions => array['UPDATE'],
|
permissions => array['edit'],
|
||||||
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipGuest(NEW),
|
hsOfficeMembershipGuest(NEW),
|
||||||
permissions => array['SELECT'],
|
permissions => array['view'],
|
||||||
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficeRelationshipTenant(newHsOfficePartnerRel), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficeRelationshipTenant(newHsOfficePartnerRel), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ execute procedure hsOfficeMembershipRbacRolesTrigger();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_membership', $idName$
|
call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$
|
||||||
'#' ||
|
'#' ||
|
||||||
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
|
(select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) ||
|
||||||
memberNumberSuffix ||
|
memberNumberSuffix ||
|
||||||
|
@ -16,7 +16,6 @@ create or replace procedure createHsOfficeMembershipTestData(
|
|||||||
declare
|
declare
|
||||||
currentTask varchar;
|
currentTask varchar;
|
||||||
relatedPartner hs_office_partner;
|
relatedPartner hs_office_partner;
|
||||||
relatedDebitorRel hs_office_relationship;
|
|
||||||
relatedDebitor hs_office_debitor;
|
relatedDebitor hs_office_debitor;
|
||||||
begin
|
begin
|
||||||
currentTask := 'creating Membership test-data ' ||
|
currentTask := 'creating Membership test-data ' ||
|
||||||
@ -28,16 +27,9 @@ begin
|
|||||||
|
|
||||||
select partner.* from hs_office_partner partner
|
select partner.* from hs_office_partner partner
|
||||||
where partner.partnerNumber = forPartnerNumber into relatedPartner;
|
where partner.partnerNumber = forPartnerNumber into relatedPartner;
|
||||||
select debitorRel.* from hs_office_relationship debitorRel
|
select d.* from hs_office_debitor d
|
||||||
join hs_office_relationship partnerRel
|
where d.partneruuid = relatedPartner.uuid
|
||||||
on debitorRel.relAnchorUuid=partnerRel.relHolderUuid and partnerRel.relType='PARTNER'
|
and d.debitorNumberSuffix = forMainDebitorNumberSuffix
|
||||||
join hs_office_partner partner
|
|
||||||
on partner.partnerRoleUuid = partnerRel.uuid
|
|
||||||
where debitorRel.relType='ACCOUNTING' -- FIXME: 'DEBITOR'
|
|
||||||
into relatedDebitorRel;
|
|
||||||
select debitor.* from hs_office_debitor debitor
|
|
||||||
where debitor.debitorRelUuid = relatedDebitorRel.uuid
|
|
||||||
and debitor.debitorNumberSuffix = forMainDebitorNumberSuffix
|
|
||||||
into relatedDebitor;
|
into relatedDebitor;
|
||||||
|
|
||||||
raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix;
|
raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix;
|
||||||
|
@ -42,8 +42,8 @@ begin
|
|||||||
|
|
||||||
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
|
-- coopsharestransactions cannot be edited nor deleted, just created+viewed
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
|
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
|
||||||
createPermissions(NEW.uuid, array ['SELECT'])
|
createPermissions(NEW.uuid, array ['view'])
|
||||||
);
|
);
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -68,7 +68,8 @@ execute procedure hsOfficeCoopSharesTransactionRbacRolesTrigger();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-coopSharesTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-coopSharesTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_coopSharesTransaction', 'target.reference');
|
call generateRbacIdentityView('hs_office_coopSharesTransaction',
|
||||||
|
idNameExpression => 'target.reference');
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ begin
|
|||||||
|
|
||||||
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
|
-- coopassetstransactions cannot be edited nor deleted, just created+viewed
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership)),
|
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
|
||||||
createPermissions(NEW.uuid, array ['SELECT'])
|
createPermissions(NEW.uuid, array ['view'])
|
||||||
);
|
);
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -68,7 +68,8 @@ execute procedure hsOfficeCoopAssetsTransactionRbacRolesTrigger();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-coopAssetsTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset hs-office-coopAssetsTransaction-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityViewFromProjection('hs_office_coopAssetsTransaction', 'target.reference');
|
call generateRbacIdentityView('hs_office_coopAssetsTransaction',
|
||||||
|
idNameExpression => 'target.reference');
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ public class ArchitectureTest {
|
|||||||
"..test",
|
"..test",
|
||||||
"..test.cust",
|
"..test.cust",
|
||||||
"..test.pac",
|
"..test.pac",
|
||||||
"..test.dom",
|
|
||||||
"..context",
|
"..context",
|
||||||
"..generated..",
|
"..generated..",
|
||||||
"..persistence..",
|
"..persistence..",
|
||||||
@ -50,8 +49,6 @@ public class ArchitectureTest {
|
|||||||
"..rbac.rbacuser",
|
"..rbac.rbacuser",
|
||||||
"..rbac.rbacgrant",
|
"..rbac.rbacgrant",
|
||||||
"..rbac.rbacrole",
|
"..rbac.rbacrole",
|
||||||
"..rbac.rbacobject",
|
|
||||||
"..rbac.rbacdef",
|
|
||||||
"..stringify"
|
"..stringify"
|
||||||
// ATTENTION: Don't simply add packages here, also add arch rules for the new package!
|
// ATTENTION: Don't simply add packages here, also add arch rules for the new package!
|
||||||
);
|
);
|
||||||
@ -119,10 +116,7 @@ public class ArchitectureTest {
|
|||||||
public static final ArchRule hsAdminPackagesRule = classes()
|
public static final ArchRule hsAdminPackagesRule = classes()
|
||||||
.that().resideInAPackage("..hs.office.(*)..")
|
.that().resideInAPackage("..hs.office.(*)..")
|
||||||
.should().onlyBeAccessed().byClassesThat()
|
.should().onlyBeAccessed().byClassesThat()
|
||||||
.resideInAnyPackage(
|
.resideInAnyPackage("..hs.office.(*)..");
|
||||||
"..hs.office.(*)..",
|
|
||||||
"..rbac.rbacgrant" // TODO: just because of RbacGrantsDiagramServiceIntegrationTest
|
|
||||||
);
|
|
||||||
|
|
||||||
@ArchTest
|
@ArchTest
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -1,37 +1,14 @@
|
|||||||
package net.hostsharing.hsadminng.context;
|
package net.hostsharing.hsadminng.context;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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 {
|
public abstract class ContextBasedTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected Context context;
|
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:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
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
|
|
||||||
);
|
|
||||||
</pre>
|
|
||||||
*/
|
|
||||||
@Autowired
|
|
||||||
protected RbacGrantsDiagramService diagramService; // just to be used in subclasses
|
|
||||||
|
|
||||||
TestInfo test;
|
TestInfo test;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
@ -109,7 +109,7 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC
|
|||||||
));
|
));
|
||||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
||||||
initialGrantNames,
|
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 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 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 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 perm view 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 role hs_office_bankaccount#sometempaccC.guest to role hs_office_bankaccount#sometempaccC.tenant by system and assume }",
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
@ -111,12 +111,12 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
"{ grant perm * on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }",
|
"{ grant perm * on hs_office_contact#anothernewcontact 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 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 global#global.admin 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 perm edit on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin 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.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 perm view 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 role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
.map(s -> s.replace("hs_office_", ""))
|
.map(s -> s.replace("hs_office_", ""))
|
||||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
"{ grant perm SELECT on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
"{ grant perm view on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
|
|||||||
.map(s -> s.replace("hs_office_", ""))
|
.map(s -> s.replace("hs_office_", ""))
|
||||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
"{ grant perm SELECT on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
"{ grant perm view on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }",
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class AddDebitor {
|
@Accepts({ "Debitor:C(Create)" })
|
||||||
|
class CreateDebitor {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_withoutAssumedRole_canAddDebitorWithBankAccount() {
|
void globalAdmin_withoutAssumedRole_canAddDebitorWithBankAccount() {
|
||||||
|
@ -9,8 +9,8 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti
|
|||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
||||||
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
|
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
|
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService;
|
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include;
|
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
|
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
|
||||||
import net.hostsharing.test.Array;
|
import net.hostsharing.test.Array;
|
||||||
import net.hostsharing.test.JpaAttempt;
|
import net.hostsharing.test.JpaAttempt;
|
||||||
@ -40,7 +40,7 @@ import static net.hostsharing.test.JpaAttempt.attempt;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@DataJpaTest
|
@DataJpaTest
|
||||||
@Import( { Context.class, JpaAttempt.class, RbacGrantsDiagramService.class })
|
@Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class })
|
||||||
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
|
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -71,7 +71,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
JpaAttempt jpaAttempt;
|
JpaAttempt jpaAttempt;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
RbacGrantsDiagramService mermaidService;
|
RbacGrantsMermaidService mermaidService;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
HttpServletRequest request;
|
HttpServletRequest request;
|
||||||
@ -137,7 +137,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
});
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
|
System.out.println("ok");
|
||||||
|
// TODO result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -189,12 +190,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
.containsExactlyInAnyOrder(Array.fromFormatted(
|
.containsExactlyInAnyOrder(Array.fromFormatted(
|
||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
// owner
|
// owner
|
||||||
"{ grant perm DELETE on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
|
"{ grant perm * 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 role global#global.admin by system and assume }",
|
||||||
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
|
"{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
|
||||||
|
|
||||||
// admin
|
// admin
|
||||||
"{ grant perm UPDATE on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
|
"{ grant perm edit 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 }",
|
"{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
|
||||||
|
|
||||||
// agent
|
// agent
|
||||||
@ -210,7 +211,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
"{ grant role contact#4th.referrer to role debitor#1000422:FeG.tenant by system and assume }",
|
"{ grant role contact#4th.referrer to role debitor#1000422:FeG.tenant by system and assume }",
|
||||||
|
|
||||||
// guest
|
// guest
|
||||||
"{ grant perm SELECT on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
|
"{ grant perm view 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 }",
|
"{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
|
||||||
|
|
||||||
null));
|
null));
|
||||||
@ -315,7 +316,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
context("superuser-alex@hostsharing.net");
|
context("superuser-alex@hostsharing.net");
|
||||||
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
|
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
|
||||||
|
|
||||||
RbacGrantsDiagramService.writeToFile("initial partner: Fourth eG + fourth contact",
|
RbacGrantsMermaidService.writeToFile("initial partner: Fourth eG + fourth contact",
|
||||||
mermaidService.allGrantsFrom(givenDebitor.getUuid(), "view", EnumSet.of(Include.USERS, Include.DETAILS)),
|
mermaidService.allGrantsFrom(givenDebitor.getUuid(), "view", EnumSet.of(Include.USERS, Include.DETAILS)),
|
||||||
"doc/all-grants-before-globalAdmin_canUpdateArbitraryDebitor.md");
|
"doc/all-grants-before-globalAdmin_canUpdateArbitraryDebitor.md");
|
||||||
|
|
||||||
|
@ -126,11 +126,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
|
|||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
|
|
||||||
// owner
|
// owner
|
||||||
"{ grant perm DELETE on membership#1000117:First to role membership#1000117:First.owner by system and assume }",
|
"{ grant perm * 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 role membership#1000117:First.owner to role global#global.admin by system and assume }",
|
||||||
|
|
||||||
// admin
|
// admin
|
||||||
"{ grant perm UPDATE on membership#1000117:First to role membership#1000117:First.admin by system and assume }",
|
"{ grant perm edit 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 role membership#1000117:First.admin to role membership#1000117:First.owner by system and assume }",
|
||||||
|
|
||||||
// agent
|
// 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 }",
|
"{ grant role membership#1000117:First.tenant to role partner#10001:First.agent by system and assume }",
|
||||||
|
|
||||||
// guest
|
// guest
|
||||||
"{ grant perm SELECT on membership#1000117:First to role membership#1000117:First.guest by system and assume }",
|
"{ grant perm view 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 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 partner#10001:First.tenant by system and assume }",
|
||||||
"{ grant role membership#1000117:First.guest to role debitor#1000111:First.tenant by system and assume }",
|
"{ grant role membership#1000117:First.guest to role debitor#1000111:First.tenant by system and assume }",
|
||||||
|
@ -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.HsOfficeRelationshipEntity;
|
||||||
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
|
||||||
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
|
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.test.JpaAttempt;
|
import net.hostsharing.test.JpaAttempt;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -421,7 +421,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
void removeEmptyPartners() {
|
void removeEmptyPartners() {
|
||||||
assumeThatWeAreImportingControlledTestData();
|
assumeThatWeAreImportingControlledTestData();
|
||||||
|
|
||||||
// avoid a error when persisting the deliberately invalid partner entry #99
|
// avoid a error when persisting the deliberetely invalid partner entry #99
|
||||||
final var idsToRemove = new HashSet<Integer>();
|
final var idsToRemove = new HashSet<Integer>();
|
||||||
partners.forEach( (id, r) -> {
|
partners.forEach( (id, r) -> {
|
||||||
final var partnerRole = r.getPartnerRole();
|
final var partnerRole = r.getPartnerRole();
|
||||||
@ -441,7 +441,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
void removeEmptyDebitors() {
|
void removeEmptyDebitors() {
|
||||||
assumeThatWeAreImportingControlledTestData();
|
assumeThatWeAreImportingControlledTestData();
|
||||||
|
|
||||||
// avoid a error when persisting the deliberately invalid partner entry #99
|
// avoid a error when persisting the deliberetely invalid partner entry #99
|
||||||
final var idsToRemove = new HashSet<Integer>();
|
final var idsToRemove = new HashSet<Integer>();
|
||||||
debitors.forEach( (id, d) -> {
|
debitors.forEach( (id, d) -> {
|
||||||
// such a record is in test data to test error messages
|
// such a record is in test data to test error messages
|
||||||
@ -523,7 +523,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persist(final Integer id, final RbacObject entity) {
|
private void persist(final Integer id, final HasUuid entity) {
|
||||||
try {
|
try {
|
||||||
//System.out.println("persisting #" + entity.hashCode() + ": " + entity);
|
//System.out.println("persisting #" + entity.hashCode() + ": " + entity);
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
@ -594,7 +594,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
}).assertSuccessful();
|
}).assertSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
private <E extends RbacObject> void updateLegacyIds(
|
private <E extends HasUuid> void updateLegacyIds(
|
||||||
Map<Integer, E> entities,
|
Map<Integer, E> entities,
|
||||||
final String legacyIdTable,
|
final String legacyIdTable,
|
||||||
final String legacyIdColumn) {
|
final String legacyIdColumn) {
|
||||||
@ -885,6 +885,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
if (relationships.values().stream()
|
if (relationships.values().stream()
|
||||||
.filter(rel -> rel.getRelAnchor() == partnerPerson && rel.getRelType() == HsOfficeRelationshipType.REPRESENTATIVE)
|
.filter(rel -> rel.getRelAnchor() == partnerPerson && rel.getRelType() == HsOfficeRelationshipType.REPRESENTATIVE)
|
||||||
.findFirst().isEmpty()) {
|
.findFirst().isEmpty()) {
|
||||||
|
//addRelationship(partnerPerson, partnerPerson, partner.getPartnerRole().getContact(), HsOfficeRelationshipType.REPRESENTATIVE);
|
||||||
contractualMissing.add(partner.getPartnerNumber());
|
contractualMissing.add(partner.getPartnerNumber());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -155,45 +155,21 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
|
|||||||
// relationship owner
|
// relationship owner
|
||||||
"{ grant perm * on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.owner 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 role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.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 }",
|
// relationship admin
|
||||||
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner 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 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 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 relationship#HostsharingeG-with-PARTNER-EBess.admin to role person#HostsharingeG.admin by system and assume }",
|
||||||
"{ grant role contact#4th.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
|
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.agent to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
|
||||||
"{ grant role person#EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
|
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.agent to role person#EBess.admin by system and assume }",
|
||||||
"{ grant role person#HostsharingeG.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
|
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.agent to role contact#4th.admin by system and assume }",
|
||||||
|
|
||||||
// owner
|
// relationship tenant
|
||||||
"{ grant perm DELETE on partner#20032:EBess-4th to role partner#20032:EBess-4th.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 DELETE on partner_details#20032:EBess-4th-details to role partner#20032:EBess-4th.owner by system and assume }",
|
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role relationship#HostsharingeG-with-PARTNER-EBess.agent by system and assume }",
|
||||||
"{ grant role partner#20032:EBess-4th.owner to role global#global.admin by system and assume }",
|
"{ grant role person#HostsharingeG.referrer to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
|
||||||
|
"{ grant role person#EBess.referrer to role relationship#HostsharingeG-with-PARTNER-EBess.tenant by system and assume }",
|
||||||
// admin
|
"{ grant role contact#4th.referrer to role relationship#HostsharingeG-with-PARTNER-EBess.tenant 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 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 }",
|
|
||||||
|
|
||||||
null)));
|
null)));
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
|||||||
final var count = personRepo.count();
|
final var count = personRepo.count();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
||||||
final var result = attempt(em, () -> toCleanup(personRepo.save(
|
final var result = attempt(em, () -> toCleanup(personRepo.save(
|
||||||
hsOfficePerson("a new person"))));
|
hsOfficePerson("a new person"))));
|
||||||
|
|
||||||
@ -113,12 +114,11 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
|||||||
"{ grant perm * on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
|
"{ grant perm * on hs_office_person#anothernewperson 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 global#global.admin and assume }",
|
"{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }",
|
||||||
"{ grant role hs_office_person#anothernewperson.owner to role global#global.admin by system 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 edit 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.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 perm view 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 role hs_office_person#anothernewperson.referrer to role hs_office_person#anothernewperson.admin by system and assume }"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -128,17 +128,18 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTestWith
|
|||||||
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
|
||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
|
|
||||||
"{ grant perm DELETE on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
|
"{ grant perm * on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
|
||||||
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to role global#global.admin by system and assume }",
|
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner to role global#global.admin by system and assume }",
|
||||||
|
|
||||||
"{ grant perm edit on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
|
"{ grant perm edit on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin by system and assume }",
|
||||||
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
|
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.owner by system and assume }",
|
||||||
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_person#ErbenBesslerMelBessler.admin by system and assume }",
|
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.admin to role hs_office_person#ErbenBesslerMelBessler.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#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.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 role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_contact#fourthcontact.admin by system and assume }",
|
||||||
|
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent to role hs_office_person#BesslerBert.admin by system and assume }",
|
||||||
|
|
||||||
"{ grant perm SELECT on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
"{ grant perm view on hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
||||||
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent by system and assume }",
|
"{ grant role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.agent by system and assume }",
|
||||||
"{ grant role hs_office_person#BesslerBert.referrer to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
"{ grant role hs_office_person#BesslerBert.referrer to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
||||||
"{ grant role hs_office_person#ErbenBesslerMelBessler.referrer to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
"{ grant role hs_office_person#ErbenBesslerMelBessler.referrer to role hs_office_relationship#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerBert.tenant by system and assume }",
|
||||||
|
@ -131,11 +131,11 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTestWithC
|
|||||||
initialGrantNames,
|
initialGrantNames,
|
||||||
|
|
||||||
// owner
|
// owner
|
||||||
"{ grant perm DELETE on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
|
"{ grant perm * 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 role sepamandate#temprefB.owner to role global#global.admin by system and assume }",
|
||||||
|
|
||||||
// admin
|
// admin
|
||||||
"{ grant perm UPDATE on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
|
"{ grant perm edit 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 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 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 }",
|
"{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
|
||||||
|
|
||||||
// guest
|
// guest
|
||||||
"{ grant perm SELECT on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }",
|
"{ grant perm view 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 }",
|
"{ grant role sepamandate#temprefB.guest to role sepamandate#temprefB.tenant by system and assume }",
|
||||||
null));
|
null));
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import net.hostsharing.hsadminng.context.ContextBasedTest;
|
|||||||
import net.hostsharing.hsadminng.persistence.HasUuid;
|
import net.hostsharing.hsadminng.persistence.HasUuid;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
|
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantRepository;
|
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.RbacRoleEntity;
|
||||||
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleRepository;
|
||||||
import net.hostsharing.test.JpaAttempt;
|
import net.hostsharing.test.JpaAttempt;
|
||||||
@ -44,7 +43,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
JpaAttempt jpaAttempt;
|
JpaAttempt jpaAttempt;
|
||||||
|
|
||||||
private TreeMap<UUID, Class<? extends RbacObject>> entitiesToCleanup = new TreeMap<>();
|
private TreeMap<UUID, Class<? extends HasUuid>> entitiesToCleanup = new TreeMap<>();
|
||||||
|
|
||||||
private static Long latestIntialTestDataSerialId;
|
private static Long latestIntialTestDataSerialId;
|
||||||
private static boolean countersInitialized = false;
|
private static boolean countersInitialized = false;
|
||||||
@ -62,7 +61,7 @@ public abstract class ContextBasedTestWithCleanup extends ContextBasedTest {
|
|||||||
return uuidToCleanup;
|
return uuidToCleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <E extends RbacObject> E toCleanup(final E entity) {
|
public <E extends HasUuid> E toCleanup(final E entity) {
|
||||||
out.println("toCleanup(" + entity.getClass() + ", " + entity.getUuid());
|
out.println("toCleanup(" + entity.getClass() + ", " + entity.getUuid());
|
||||||
if ( entity.getUuid() == null ) {
|
if ( entity.getUuid() == null ) {
|
||||||
throw new IllegalArgumentException("only persisted entities with valid uuid allowed");
|
throw new IllegalArgumentException("only persisted entities with valid uuid allowed");
|
||||||
|
@ -73,16 +73,14 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
// TODO: should there be a grantedByRole or just a grantedByTrigger?
|
hasEntry("grantedByRoleIdName", "global#global.admin"),
|
||||||
hasEntry("grantedByRoleIdName", "test_customer#xxx.owner"),
|
|
||||||
hasEntry("grantedRoleIdName", "test_customer#xxx.admin"),
|
hasEntry("grantedRoleIdName", "test_customer#xxx.admin"),
|
||||||
hasEntry("granteeUserName", "customer-admin@xxx.example.com")
|
hasEntry("granteeUserName", "customer-admin@xxx.example.com")
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
// TODO: should there be a grantedByRole or just a grantedByTrigger?
|
hasEntry("grantedByRoleIdName", "global#global.admin"),
|
||||||
hasEntry("grantedByRoleIdName", "test_customer#yyy.owner"),
|
|
||||||
hasEntry("grantedRoleIdName", "test_customer#yyy.admin"),
|
hasEntry("grantedRoleIdName", "test_customer#yyy.admin"),
|
||||||
hasEntry("granteeUserName", "customer-admin@yyy.example.com")
|
hasEntry("granteeUserName", "customer-admin@yyy.example.com")
|
||||||
)
|
)
|
||||||
@ -298,7 +296,7 @@ class RbacGrantControllerAcceptanceTest extends ContextBasedTest {
|
|||||||
result.assertThat()
|
result.assertThat()
|
||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.body("message", containsString("Access to granted role"))
|
.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))
|
assertThat(findAllGrantsOf(givenCurrentUserAsPackageAdmin))
|
||||||
.extracting(RbacGrantEntity::getGranteeUserName)
|
.extracting(RbacGrantEntity::getGranteeUserName)
|
||||||
.doesNotContain(givenNewUser.getName());
|
.doesNotContain(givenNewUser.getName());
|
||||||
|
@ -84,7 +84,7 @@ class RbacGrantRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
// then
|
// then
|
||||||
exactlyTheseRbacGrantsAreReturned(
|
exactlyTheseRbacGrantsAreReturned(
|
||||||
result,
|
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_customer#xxx.admin to user customer-admin@xxx.example.com by role global#global.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 }",
|
"{ 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#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_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
|
// then
|
||||||
attempt.assertExceptionWithRootCauseMessage(
|
attempt.assertExceptionWithRootCauseMessage(
|
||||||
JpaSystemException.class,
|
JpaSystemException.class,
|
||||||
"ERROR: [403] Access to granted role test_package#xxx00.owner",
|
"ERROR: [403] Access to granted role " + given.packageOwnerRoleUuid
|
||||||
"forbidden for test_package#xxx00.admin");
|
+ " forbidden for {test_package#xxx00.admin}");
|
||||||
jpaAttempt.transacted(() -> {
|
jpaAttempt.transacted(() -> {
|
||||||
// finally, we use the new user to make sure, no roles were granted
|
// finally, we use the new user to make sure, no roles were granted
|
||||||
context(given.arbitraryUser.getName(), null);
|
context(given.arbitraryUser.getName(), null);
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,136 @@
|
|||||||
|
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.RbacGrantsMermaidService.Include;
|
||||||
|
import net.hostsharing.test.JpaAttempt;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
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, RbacGrantsMermaidService.class})
|
||||||
|
class RbacGrantsMermaidServiceIntegrationTest extends ContextBasedTestWithCleanup {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RbacGrantsMermaidService grantsMermaidService;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
HttpServletRequest request;
|
||||||
|
|
||||||
|
@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_package#xxx00.tenant[
|
||||||
|
test_package
|
||||||
|
xxx00.t
|
||||||
|
tenant] --> role:test_customer#xxx.tenant[
|
||||||
|
test_customer
|
||||||
|
xxx.t
|
||||||
|
tenant]
|
||||||
|
role:test_domain#xxx00-aaaa.owner[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.o
|
||||||
|
owner] --> role:test_domain#xxx00-aaaa.admin[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.a
|
||||||
|
admin]
|
||||||
|
role:test_domain#xxx00-aaaa.admin[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.a
|
||||||
|
admin] --> role:test_package#xxx00.tenant[
|
||||||
|
test_package
|
||||||
|
xxx00.t
|
||||||
|
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_domain#xxx00-aaaa.owner[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.o
|
||||||
|
owner] --> perm:*:on:test_domain#xxx00-aaaa{{
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa
|
||||||
|
*}}
|
||||||
|
role:test_customer#xxx.tenant[
|
||||||
|
test_customer
|
||||||
|
xxx.t
|
||||||
|
tenant] --> perm:view:on:test_customer#xxx{{
|
||||||
|
test_customer
|
||||||
|
xxx
|
||||||
|
view}}
|
||||||
|
role:test_domain#xxx00-aaaa.admin[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.a
|
||||||
|
admin] --> perm:edit:on:test_domain#xxx00-aaaa{{
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa
|
||||||
|
edit}}
|
||||||
|
role:test_package#xxx00.tenant[
|
||||||
|
test_package
|
||||||
|
xxx00.t
|
||||||
|
tenant] --> role:test_customer#xxx.tenant[
|
||||||
|
test_customer
|
||||||
|
xxx.t
|
||||||
|
tenant]
|
||||||
|
role:test_domain#xxx00-aaaa.owner[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.o
|
||||||
|
owner] --> role:test_domain#xxx00-aaaa.admin[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.a
|
||||||
|
admin]
|
||||||
|
role:test_package#xxx00.tenant[
|
||||||
|
test_package
|
||||||
|
xxx00.t
|
||||||
|
tenant] --> perm:view:on:test_package#xxx00{{
|
||||||
|
test_package
|
||||||
|
xxx00
|
||||||
|
view}}
|
||||||
|
role:test_domain#xxx00-aaaa.admin[
|
||||||
|
test_domain
|
||||||
|
xxx00-aaaa.a
|
||||||
|
admin] --> role:test_package#xxx00.tenant[
|
||||||
|
test_package
|
||||||
|
xxx00.t
|
||||||
|
tenant]
|
||||||
|
""".trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
// @Disabled
|
||||||
|
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));
|
||||||
|
|
||||||
|
RbacGrantsMermaidService.writeToFile(join(";", context.getAssumedRoles()), graph, "doc/all-grants.md");
|
||||||
|
}
|
||||||
|
}
|
@ -288,15 +288,19 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "SELECT"))
|
hasEntry("op", "view"))
|
||||||
|
))
|
||||||
|
.body("", hasItem(
|
||||||
|
allOf(
|
||||||
|
hasEntry("roleName", "test_package#yyy00.admin"),
|
||||||
|
hasEntry("op", "add-domain"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "DELETE"))
|
hasEntry("op", "*"))
|
||||||
))
|
))
|
||||||
// actual content tested in integration test, so this is enough for here:
|
.body("size()", is(7));
|
||||||
.body("size()", greaterThanOrEqualTo(6));
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +313,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
RestAssured
|
RestAssured
|
||||||
.given()
|
.given()
|
||||||
.header("current-user", "superuser-alex@hostsharing.net")
|
.header("current-user", "superuser-alex@hostsharing.net")
|
||||||
.header("assumed-roles", "test_customer#yyy.admin")
|
.header("assumed-roles", "test_package#yyy00.admin")
|
||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions")
|
.get("http://localhost/api/rbac/users/" + givenUser.getUuid() + "/permissions")
|
||||||
@ -319,15 +323,19 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "SELECT"))
|
hasEntry("op", "view"))
|
||||||
|
))
|
||||||
|
.body("", hasItem(
|
||||||
|
allOf(
|
||||||
|
hasEntry("roleName", "test_package#yyy00.admin"),
|
||||||
|
hasEntry("op", "add-domain"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "DELETE"))
|
hasEntry("op", "*"))
|
||||||
))
|
))
|
||||||
// actual content tested in integration test, so this is enough for here:
|
.body("size()", is(7));
|
||||||
.body("size()", greaterThanOrEqualTo(6));
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,15 +357,19 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "SELECT"))
|
hasEntry("op", "view"))
|
||||||
|
))
|
||||||
|
.body("", hasItem(
|
||||||
|
allOf(
|
||||||
|
hasEntry("roleName", "test_package#yyy00.admin"),
|
||||||
|
hasEntry("op", "add-domain"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "DELETE"))
|
hasEntry("op", "*"))
|
||||||
))
|
))
|
||||||
// actual content tested in integration test, so this is enough for here:
|
.body("size()", is(7));
|
||||||
.body("size()", greaterThanOrEqualTo(6));
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.util.Comparator.comparing;
|
|
||||||
import static net.hostsharing.test.JpaAttempt.attempt;
|
import static net.hostsharing.test.JpaAttempt.attempt;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@ -182,48 +181,50 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
|
|
||||||
private static final String[] ALL_USER_PERMISSIONS = Array.of(
|
private static final String[] ALL_USER_PERMISSIONS = Array.of(
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#xxx.admin -> test_customer#xxx: SELECT",
|
"global#global.admin -> global#global: add-customer",
|
||||||
"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#xxx.admin -> test_customer#xxx: add-package",
|
||||||
"test_customer#yyy.owner -> test_customer#yyy: DELETE",
|
"test_customer#xxx.admin -> test_customer#xxx: view",
|
||||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT",
|
"test_customer#xxx.owner -> test_customer#xxx: *",
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
|
"test_customer#xxx.tenant -> test_customer#xxx: view",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
"test_package#yyy00.tenant -> test_package#yyy00: SELECT",
|
"test_package#xxx00.tenant -> test_package#xxx00: view",
|
||||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
"test_package#xxx01.admin -> test_package#xxx01: add-domain",
|
||||||
"test_package#yyy01.admin -> test_package#yyy01: INSERT:test_domain",
|
"test_package#xxx01.admin -> test_package#xxx01: add-domain",
|
||||||
"test_package#yyy01.tenant -> test_package#yyy01: SELECT",
|
"test_package#xxx01.tenant -> test_package#xxx01: view",
|
||||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
"test_package#xxx02.admin -> test_package#xxx02: add-domain",
|
||||||
"test_package#yyy02.admin -> test_package#yyy02: INSERT:test_domain",
|
"test_package#xxx02.admin -> test_package#xxx02: add-domain",
|
||||||
"test_package#yyy02.tenant -> test_package#yyy02: SELECT",
|
"test_package#xxx02.tenant -> test_package#xxx02: view",
|
||||||
|
|
||||||
"test_customer#zzz.admin -> test_customer#zzz: SELECT",
|
"test_customer#yyy.admin -> test_customer#yyy: add-package",
|
||||||
"test_customer#zzz.owner -> test_customer#zzz: DELETE",
|
"test_customer#yyy.admin -> test_customer#yyy: view",
|
||||||
"test_customer#zzz.tenant -> test_customer#zzz: SELECT",
|
"test_customer#yyy.owner -> test_customer#yyy: *",
|
||||||
"test_customer#zzz.admin -> test_customer#zzz: INSERT:test_package",
|
"test_customer#yyy.tenant -> test_customer#yyy: view",
|
||||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#zzz00.admin -> test_package#zzz00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#zzz00.tenant -> test_package#zzz00: SELECT",
|
"test_package#yyy00.tenant -> test_package#yyy00: view",
|
||||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
"test_package#yyy01.admin -> test_package#yyy01: add-domain",
|
||||||
"test_package#zzz01.admin -> test_package#zzz01: INSERT:test_domain",
|
"test_package#yyy01.admin -> test_package#yyy01: add-domain",
|
||||||
"test_package#zzz01.tenant -> test_package#zzz01: SELECT",
|
"test_package#yyy01.tenant -> test_package#yyy01: view",
|
||||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
"test_package#yyy02.admin -> test_package#yyy02: add-domain",
|
||||||
"test_package#zzz02.admin -> test_package#zzz02: INSERT:test_domain",
|
"test_package#yyy02.admin -> test_package#yyy02: add-domain",
|
||||||
"test_package#zzz02.tenant -> test_package#zzz02: SELECT"
|
"test_package#yyy02.tenant -> test_package#yyy02: view",
|
||||||
// @formatter:on
|
|
||||||
|
"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
|
@Test
|
||||||
@ -232,9 +233,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
context("superuser-alex@hostsharing.net");
|
context("superuser-alex@hostsharing.net");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-fran@hostsharing.net"))
|
final var result = rbacUserRepository.findPermissionsOfUserByUuid(userUUID("superuser-alex@hostsharing.net"));
|
||||||
.stream().filter(p -> p.getObjectTable().contains("test_"))
|
|
||||||
.sorted(comparing(RbacUserPermission::toString)).toList();
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
|
allTheseRbacPermissionsAreReturned(result, ALL_USER_PERMISSIONS);
|
||||||
@ -252,32 +251,32 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
allTheseRbacPermissionsAreReturned(
|
allTheseRbacPermissionsAreReturned(
|
||||||
result,
|
result,
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#xxx.admin -> test_customer#xxx: INSERT:test_package",
|
"test_customer#xxx.admin -> test_customer#xxx: add-package",
|
||||||
"test_customer#xxx.admin -> test_customer#xxx: SELECT",
|
"test_customer#xxx.admin -> test_customer#xxx: view",
|
||||||
"test_customer#xxx.tenant -> test_customer#xxx: SELECT",
|
"test_customer#xxx.tenant -> test_customer#xxx: view",
|
||||||
|
|
||||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
"test_package#xxx00.tenant -> test_package#xxx00: SELECT",
|
"test_package#xxx00.tenant -> test_package#xxx00: view",
|
||||||
"test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
|
"test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: *",
|
||||||
|
|
||||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
"test_package#xxx01.admin -> test_package#xxx01: add-domain",
|
||||||
"test_package#xxx01.admin -> test_package#xxx01: INSERT:test_domain",
|
"test_package#xxx01.admin -> test_package#xxx01: add-domain",
|
||||||
"test_package#xxx01.tenant -> test_package#xxx01: SELECT",
|
"test_package#xxx01.tenant -> test_package#xxx01: view",
|
||||||
"test_domain#xxx01-aaaa.owner -> test_domain#xxx01-aaaa: DELETE",
|
"test_domain#xxx01-aaaa.owner -> test_domain#xxx01-aaaa: *",
|
||||||
|
|
||||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
"test_package#xxx02.admin -> test_package#xxx02: add-domain",
|
||||||
"test_package#xxx02.admin -> test_package#xxx02: INSERT:test_domain",
|
"test_package#xxx02.admin -> test_package#xxx02: add-domain",
|
||||||
"test_package#xxx02.tenant -> test_package#xxx02: SELECT",
|
"test_package#xxx02.tenant -> test_package#xxx02: view",
|
||||||
"test_domain#xxx02-aaaa.owner -> test_domain#xxx02-aaaa: DELETE"
|
"test_domain#xxx02-aaaa.owner -> test_domain#xxx02-aaaa: *"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
noneOfTheseRbacPermissionsAreReturned(
|
noneOfTheseRbacPermissionsAreReturned(
|
||||||
result,
|
result,
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
|
"test_customer#yyy.admin -> test_customer#yyy: add-package",
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: SELECT",
|
"test_customer#yyy.admin -> test_customer#yyy: view",
|
||||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT"
|
"test_customer#yyy.tenant -> test_customer#yyy: view"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -312,26 +311,26 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
allTheseRbacPermissionsAreReturned(
|
allTheseRbacPermissionsAreReturned(
|
||||||
result,
|
result,
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#xxx.tenant -> test_customer#xxx: SELECT",
|
"test_customer#xxx.tenant -> test_customer#xxx: view",
|
||||||
// "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
|
// "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: add-domain",
|
||||||
"test_package#xxx00.admin -> test_package#xxx00: INSERT:test_domain",
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
"test_package#xxx00.tenant -> test_package#xxx00: SELECT",
|
"test_package#xxx00.tenant -> test_package#xxx00: view",
|
||||||
"test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: DELETE",
|
"test_domain#xxx00-aaaa.owner -> test_domain#xxx00-aaaa: *",
|
||||||
"test_domain#xxx00-aaab.owner -> test_domain#xxx00-aaab: DELETE"
|
"test_domain#xxx00-aaab.owner -> test_domain#xxx00-aaab: *"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
noneOfTheseRbacPermissionsAreReturned(
|
noneOfTheseRbacPermissionsAreReturned(
|
||||||
result,
|
result,
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: INSERT:test_package",
|
"test_customer#yyy.admin -> test_customer#yyy: add-package",
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: SELECT",
|
"test_customer#yyy.admin -> test_customer#yyy: view",
|
||||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT",
|
"test_customer#yyy.tenant -> test_customer#yyy: view",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#yyy00.tenant -> test_package#yyy00: SELECT",
|
"test_package#yyy00.tenant -> test_package#yyy00: view",
|
||||||
"test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
|
"test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: *",
|
||||||
"test_domain#yyy00-aaab.owner -> test_domain#yyy00-aaab: DELETE"
|
"test_domain#yyy00-aaab.owner -> test_domain#yyy00-aaab: *"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -360,10 +359,11 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
allTheseRbacPermissionsAreReturned(
|
allTheseRbacPermissionsAreReturned(
|
||||||
result,
|
result,
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
"test_customer#xxx.tenant -> test_customer#xxx: SELECT",
|
"test_customer#xxx.tenant -> test_customer#xxx: view",
|
||||||
// "test_customer#xxx.admin -> test_customer#xxx: view" - Not permissions through the customer admin!
|
// "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: add-domain",
|
||||||
"test_package#xxx00.tenant -> test_package#xxx00: SELECT"
|
"test_package#xxx00.admin -> test_package#xxx00: add-domain",
|
||||||
|
"test_package#xxx00.tenant -> test_package#xxx00: view"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
noneOfTheseRbacPermissionsAreReturned(
|
noneOfTheseRbacPermissionsAreReturned(
|
||||||
@ -373,13 +373,13 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
"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
|
// no permissions on other customer's objects
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: add-package",
|
"test_customer#yyy.admin -> test_customer#yyy: add-package",
|
||||||
"test_customer#yyy.admin -> test_customer#yyy: SELECT",
|
"test_customer#yyy.admin -> test_customer#yyy: view",
|
||||||
"test_customer#yyy.tenant -> test_customer#yyy: SELECT",
|
"test_customer#yyy.tenant -> test_customer#yyy: view",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#yyy00.admin -> test_package#yyy00: INSERT:test_domain",
|
"test_package#yyy00.admin -> test_package#yyy00: add-domain",
|
||||||
"test_package#yyy00.tenant -> test_package#yyy00: SELECT",
|
"test_package#yyy00.tenant -> test_package#yyy00: view",
|
||||||
"test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: DELETE",
|
"test_domain#yyy00-aaaa.owner -> test_domain#yyy00-aaaa: *",
|
||||||
"test_domain#yyy00-xxxb.owner -> test_domain#yyy00-xxxb: DELETE"
|
"test_domain#yyy00-xxxb.owner -> test_domain#yyy00-xxxb: *"
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -432,8 +432,7 @@ class RbacUserRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
final List<RbacUserPermission> actualResult,
|
final List<RbacUserPermission> actualResult,
|
||||||
final String... expectedRoleNames) {
|
final String... expectedRoleNames) {
|
||||||
assertThat(actualResult)
|
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);
|
.contains(expectedRoleNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
// finally, the new customer can be viewed by its own admin
|
// finally, the new customer can be viewed by its own admin
|
||||||
final var newUserUuid = UUID.fromString(
|
final var newUserUuid = UUID.fromString(
|
||||||
location.substring(location.lastIndexOf('/') + 1));
|
location.substring(location.lastIndexOf('/') + 1));
|
||||||
context.define("superuser-fran@hostsharing.net", "test_customer#uuu.admin");
|
context.define("customer-admin@uuu.example.com");
|
||||||
assertThat(testCustomerRepository.findByUuid(newUserUuid))
|
assertThat(testCustomerRepository.findByUuid(newUserUuid))
|
||||||
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("uuu"));
|
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("uuu"));
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.body("message", containsString("insert into test_customer not allowed for current subjects {test_customer#xxx.admin}"));
|
.body("message", containsString("add-customer not permitted for test_customer#xxx.admin"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// finally, the new customer was not created
|
// finally, the new customer was not created
|
||||||
@ -204,7 +204,7 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.statusCode(403)
|
.statusCode(403)
|
||||||
.body("message", containsString("insert into test_customer not allowed for current subjects {customer-admin@yyy.example.com}"));
|
.body("message", containsString("add-customer not permitted for customer-admin@yyy.example.com"));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// finally, the new customer was not created
|
// finally, the new customer was not created
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
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
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,6 +10,8 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
|||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.PersistenceContext;
|
||||||
import jakarta.persistence.PersistenceException;
|
import jakarta.persistence.PersistenceException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -25,6 +27,9 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
TestCustomerRepository testCustomerRepository;
|
TestCustomerRepository testCustomerRepository;
|
||||||
|
|
||||||
|
@PersistenceContext
|
||||||
|
EntityManager em;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
HttpServletRequest request;
|
HttpServletRequest request;
|
||||||
|
|
||||||
@ -38,6 +43,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
final var count = testCustomerRepository.count();
|
final var count = testCustomerRepository.count();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
||||||
final var result = attempt(em, () -> {
|
final var result = attempt(em, () -> {
|
||||||
final var newCustomer = new TestCustomerEntity(
|
final var newCustomer = new TestCustomerEntity(
|
||||||
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
|
UUID.randomUUID(), "www", 90001, "customer-admin@www.example.com");
|
||||||
@ -66,7 +72,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(
|
result.assertExceptionWithRootCauseMessage(
|
||||||
PersistenceException.class,
|
PersistenceException.class,
|
||||||
"ERROR: [403] insert into test_customer not allowed for current subjects {test_customer#xxx.admin}");
|
"add-customer not permitted for test_customer#xxx.admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -84,7 +90,7 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(
|
result.assertExceptionWithRootCauseMessage(
|
||||||
PersistenceException.class,
|
PersistenceException.class,
|
||||||
"ERROR: [403] insert into test_customer not allowed for current subjects {customer-admin@xxx.example.com}");
|
"add-customer not permitted for customer-admin@xxx.example.com");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,15 +116,15 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void globalAdmin_withAssumedCustomerOwnerRole_canViewExactlyThatCustomer() {
|
public void globalAdmin_withAssumedglobalAdminRole_canViewAllCustomers() {
|
||||||
given:
|
given:
|
||||||
context("superuser-alex@hostsharing.net", "test_customer#yyy.owner");
|
context("superuser-alex@hostsharing.net", "global#global.admin");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
|
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
|
||||||
|
|
||||||
then:
|
then:
|
||||||
allTheseCustomersAreReturned(result, "yyy");
|
allTheseCustomersAreReturned(result, "xxx", "yyy", "zzz");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -135,8 +141,6 @@ class TestCustomerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customerAdmin_withAssumedOwnedPackageAdminRole_canViewOnlyItsOwnCustomer() {
|
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);
|
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(null);
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.test.pac;
|
package net.hostsharing.hsadminng.test.pac;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
import net.hostsharing.hsadminng.context.ContextBasedTest;
|
|
||||||
import net.hostsharing.test.JpaAttempt;
|
import net.hostsharing.test.JpaAttempt;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -20,7 +19,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
|
|
||||||
@DataJpaTest
|
@DataJpaTest
|
||||||
@Import( { Context.class, JpaAttempt.class })
|
@Import( { Context.class, JpaAttempt.class })
|
||||||
class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
|
class TestPackageRepositoryIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
Context context;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TestPackageRepository testPackageRepository;
|
TestPackageRepository testPackageRepository;
|
||||||
@ -38,10 +40,9 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
class FindAllByOptionalNameLike {
|
class FindAllByOptionalNameLike {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void globalAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
|
public void globalAdmin_withoutAssumedRole_canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
|
||||||
// given
|
// given
|
||||||
// alex is not just global-admin but lso the creating user, thus we use fran
|
context.define("superuser-alex@hostsharing.net");
|
||||||
context.define("superuser-fran@hostsharing.net");
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final var result = testPackageRepository.findAllByOptionalNameLike(null);
|
final var result = testPackageRepository.findAllByOptionalNameLike(null);
|
||||||
@ -51,7 +52,7 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotAssumed() {
|
public void globalAdmin_withAssumedglobalAdminRole__canNotViewAnyPackages_becauseThoseGrantsAreNotassumedd() {
|
||||||
given:
|
given:
|
||||||
context.define("superuser-alex@hostsharing.net", "global#global.admin");
|
context.define("superuser-alex@hostsharing.net", "global#global.admin");
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ class TestPackageRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
class OptimisticLocking {
|
class OptimisticLocking {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportsOptimisticLocking() {
|
public void supportsOptimisticLocking() throws InterruptedException {
|
||||||
// given
|
// given
|
||||||
globalAdminWithAssumedRole("test_package#xxx00.admin");
|
globalAdminWithAssumedRole("test_package#xxx00.admin");
|
||||||
final var pac = testPackageRepository.findAllByOptionalNameLike("%").get(0);
|
final var pac = testPackageRepository.findAllByOptionalNameLike("%").get(0);
|
||||||
|
@ -4,8 +4,8 @@ spring:
|
|||||||
platform: postgres
|
platform: postgres
|
||||||
|
|
||||||
datasource:
|
datasource:
|
||||||
url-tc: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
|
url: jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers
|
||||||
url: jdbc:postgresql://localhost:5432/postgres
|
url-local: jdbc:postgresql://localhost:5432/postgres
|
||||||
username: postgres
|
username: postgres
|
||||||
password: password
|
password: password
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user