RBAC Diagram+PostgreSQL Generator #21
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. view or edit) on a specific (business-) object.
|
A permission allows a specific operation (e.g. SELECT or UPDATE) on a specific (business-) object.
|
||||||
|
|
||||||
You can find the entity structure as a UML class diagram as follows:
|
You can find the entity structure as a UML class diagram as follows:
|
||||||
|
|
||||||
@ -101,13 +101,12 @@ package RBAC {
|
|||||||
RbacPermission *-- RbacObject
|
RbacPermission *-- RbacObject
|
||||||
|
|
||||||
enum RbacOperation {
|
enum RbacOperation {
|
||||||
add-package
|
INSERT:package
|
||||||
add-domain
|
INSERT:domain
|
||||||
add-domain
|
|
||||||
...
|
...
|
||||||
view
|
SELECT
|
||||||
edit
|
UPDATE
|
||||||
delete
|
DELETE
|
||||||
}
|
}
|
||||||
|
|
||||||
entity RbacObject {
|
entity RbacObject {
|
||||||
@ -172,11 +171,10 @@ 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:
|
||||||
|
|
||||||
- **'add-...'** - permits creating new instances of specific entity types underneath the object specified by the permission, e.g. "add-package"
|
- **'INSERT'** - permits inserting new rows related to the row, to which the permission belongs, in the table which is specified an extra column
|
||||||
- **'view'** - permits reading the contents of the object specified by the permission
|
- **'SELECT'** - permits selecting the row specified by the permission
|
||||||
- **'edit'** - change the contents of the object specified by the permission
|
- **'UPDATE'** - permits updating (only the updatable columns of) the row specified by the permission
|
||||||
- **'delete'** - delete the object specified by the permission
|
- **'DELETE'** - permits deleting the row 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.
|
||||||
|
|
||||||
@ -212,7 +210,7 @@ E.g. for a new *customer* it would be granted to 'administrators' and for a new
|
|||||||
|
|
||||||
Whoever has the owner-role assigned can do everything with the related business-object, including deleting (or deactivating) it.
|
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
|
||||||
@ -220,14 +218,14 @@ By this, all roles ob sub-objects, which are assigned to the 'admin' role, are a
|
|||||||
The admin-role is granted to a role of those subjects who manage the business object.
|
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 edit the related business-object but not deleting (or deactivating) it.
|
Whoever has the admin-role assigned, can usually update the related business-object but not delete (or deactivating) it.
|
||||||
|
|
||||||
The admin-role also comprises lesser roles, through which the view-permission is granted.
|
The admin-role also comprises lesser roles, through which the SELECT-permission is granted.
|
||||||
|
|
||||||
#### agent
|
#### 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 edit it.
|
It's usually granted to those roles and users who represent the related business-object, but are not allowed to update it.
|
||||||
|
|
||||||
Other than the tenant-role, it usually offers broader visibility of sub-business-objects (joined entities).
|
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,
|
||||||
@ -235,19 +233,19 @@ but not its banking data.
|
|||||||
|
|
||||||
#### tenant
|
#### tenant
|
||||||
|
|
||||||
The tenant-role is granted to everybody who needs to be able to view the business-object and (probably some) related business-objects.
|
The tenant-role is granted to everybody who needs to be able to select the business-object and (probably some) related business-objects.
|
||||||
Usually all owners, admins and tenants of sub-objects get this role granted.
|
Usually all owners, admins and tenants of sub-objects get this role granted.
|
||||||
|
|
||||||
Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get view permission.
|
Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get SELECT permission.
|
||||||
|
|
||||||
#### guest
|
#### 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 view-permission is granted to it, instead of to the tenant-role.
|
If the guest-role exists, the SELECT-permission is granted to it, instead of to the tenant-role.
|
||||||
Other than the tenant-role, the guest-roles does never grant any roles of related objects.
|
Other than the tenant-role, the guest-roles does never grant any roles of related objects.
|
||||||
|
|
||||||
Also, if the guest-role exists, the tenant-role receives the view-permission through the guest-role.
|
Also, if the guest-role exists, the tenant-role receives the SELECT-permission through the guest-role.
|
||||||
|
|
||||||
|
|
||||||
### Referenced Business Objects and Role-Depreciation
|
### Referenced Business Objects and Role-Depreciation
|
||||||
@ -263,7 +261,7 @@ The admin-role of one object could be granted visibility to another object throu
|
|||||||
|
|
||||||
But not in all cases role-depreciation takes place.
|
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 view sub-objects.
|
because it should be again allowed to select sub-objects.
|
||||||
The same for the agent-role, often it is granted another agent-role.
|
The same for the agent-role, often it is granted another agent-role.
|
||||||
|
|
||||||
|
|
||||||
@ -297,14 +295,14 @@ package RbacRoles {
|
|||||||
RbacUsers -[hidden]> RbacRoles
|
RbacUsers -[hidden]> RbacRoles
|
||||||
|
|
||||||
package RbacPermissions {
|
package RbacPermissions {
|
||||||
object PermCustXyz_View
|
object PermCustXyz_SELECT
|
||||||
object PermCustXyz_Edit
|
object PermCustXyz_UPDATE
|
||||||
object PermCustXyz_Delete
|
object PermCustXyz_DELETE
|
||||||
object PermCustXyz_AddPackage
|
object PermCustXyz_INSERT:Package
|
||||||
object PermPackXyz00_View
|
object PermPackXyz00_SELECT
|
||||||
object PermPackXyz00_Edit
|
object PermPackXyz00_EDIT
|
||||||
object PermPackXyz00_Delete
|
object PermPackXyz00_DELETE
|
||||||
object PermPackXyz00_AddUser
|
object PermPackXyz00_INSERT:USER
|
||||||
}
|
}
|
||||||
RbacRoles -[hidden]> RbacPermissions
|
RbacRoles -[hidden]> RbacPermissions
|
||||||
|
|
||||||
@ -322,23 +320,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_Edit
|
RoleCustXyz_Owner o--> PermCustXyz_UPDATE
|
||||||
RoleCustXyz_Owner o--> PermCustXyz_Delete
|
RoleCustXyz_Owner o--> PermCustXyz_DELETE
|
||||||
RoleCustXyz_Admin o--> PermCustXyz_View
|
RoleCustXyz_Admin o--> PermCustXyz_SELECT
|
||||||
RoleCustXyz_Admin o--> PermCustXyz_AddPackage
|
RoleCustXyz_Admin o--> PermCustXyz_INSERT:Package
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_View
|
RolePackXyz00_Owner o--> PermPackXyz00_SELECT
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_Edit
|
RolePackXyz00_Owner o--> PermPackXyz00_UPDATE
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_Delete
|
RolePackXyz00_Owner o--> PermPackXyz00_DELETE
|
||||||
RolePackXyz00_Owner o--> PermPackXyz00_AddUser
|
RolePackXyz00_Owner o--> PermPackXyz00_INSERT:User
|
||||||
|
|
||||||
PermCustXyz_View o--> CustXyz
|
PermCustXyz_SELECT o--> CustXyz
|
||||||
PermCustXyz_Edit o--> CustXyz
|
PermCustXyz_UPDATE o--> CustXyz
|
||||||
PermCustXyz_Delete o--> CustXyz
|
PermCustXyz_DELETE o--> CustXyz
|
||||||
PermCustXyz_AddPackage o--> CustXyz
|
PermCustXyz_INSERT:Package o--> CustXyz
|
||||||
PermPackXyz00_View o--> PackXyz00
|
PermPackXyz00_SELECT o--> PackXyz00
|
||||||
PermPackXyz00_Edit o--> PackXyz00
|
PermPackXyz00_UPDATE o--> PackXyz00
|
||||||
PermPackXyz00_Delete o--> PackXyz00
|
PermPackXyz00_DELETE o--> PackXyz00
|
||||||
PermPackXyz00_AddUser o--> PackXyz00
|
PermPackXyz00_INSERT:User o--> PackXyz00
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
@ -353,12 +351,12 @@ To support the RBAC system, for each business-object-table, some more artifacts
|
|||||||
|
|
||||||
Not yet implemented, but planned are these actions:
|
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 'edit' right,
|
- an `ON UPDATE ... DO INSTEAD` rule to allow `SQL UPDATE` if the user has 'UPDATE' right,
|
||||||
- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has 'add-..' right to the parent-business-object.
|
- an `ON INSERT ... DO INSTEAD` rule to allow `SQL INSERT` if the user has the 'INSERT' right for the parent-business-object.
|
||||||
|
|
||||||
The restricted view takes the current user from a session property and applies the hierarchy of its roles all the way down to the permissions related to the respective business-object-table.
|
The restricted view takes the current user from a session property and applies the hierarchy of its roles all the way down to the permissions related to the respective business-object-table.
|
||||||
This way, each user can only view the data they have 'view'-permission for, only create those they have 'add-...'-permission, only update those they have 'edit'- and only delete those they have 'delete'-permission to.
|
This way, each user can only select the data they have 'SELECT'-permission for, only create those they have 'add-...'-permission, only update those they have 'UPDATE'- and only delete those they have 'DELETE'-permission to.
|
||||||
|
|
||||||
### Current User
|
### Current User
|
||||||
|
|
||||||
@ -458,26 +456,26 @@ allow_mixing
|
|||||||
entity "BObj customer#xyz" as boCustXyz
|
entity "BObj customer#xyz" as boCustXyz
|
||||||
|
|
||||||
together {
|
together {
|
||||||
entity "Perm customer#xyz *" as permCustomerXyzAll
|
entity "Perm customer#xyz *" as permCustomerXyzDELETE
|
||||||
permCustomerXyzAll --> boCustXyz
|
permCustomerXyzDELETE --> boCustXyz
|
||||||
|
|
||||||
entity "Perm customer#xyz add-package" as permCustomerXyzAddPack
|
entity "Perm customer#xyz INSERT:package" as permCustomerXyzINSERT:package
|
||||||
permCustomerXyzAddPack --> boCustXyz
|
permCustomerXyzINSERT:package --> boCustXyz
|
||||||
|
|
||||||
entity "Perm customer#xyz view" as permCustomerXyzView
|
entity "Perm customer#xyz SELECT" as permCustomerXyzSELECT
|
||||||
permCustomerXyzView --> boCustXyz
|
permCustomerXyzSELECT--> boCustXyz
|
||||||
}
|
}
|
||||||
|
|
||||||
entity "Role customer#xyz.tenant" as roleCustXyzTenant
|
entity "Role customer#xyz.tenant" as roleCustXyzTenant
|
||||||
roleCustXyzTenant --> permCustomerXyzView
|
roleCustXyzTenant --> permCustomerXyzSELECT
|
||||||
|
|
||||||
entity "Role customer#xyz.admin" as roleCustXyzAdmin
|
entity "Role customer#xyz.admin" as roleCustXyzAdmin
|
||||||
roleCustXyzAdmin --> roleCustXyzTenant
|
roleCustXyzAdmin --> roleCustXyzTenant
|
||||||
roleCustXyzAdmin --> permCustomerXyzAddPack
|
roleCustXyzAdmin --> permCustomerXyzINSERT:package
|
||||||
|
|
||||||
entity "Role customer#xyz.owner" as roleCustXyzOwner
|
entity "Role customer#xyz.owner" as roleCustXyzOwner
|
||||||
roleCustXyzOwner ..> roleCustXyzAdmin
|
roleCustXyzOwner ..> roleCustXyzAdmin
|
||||||
roleCustXyzOwner --> permCustomerXyzAll
|
roleCustXyzOwner --> permCustomerXyzDELETE
|
||||||
|
|
||||||
actor "Customer XYZ Admin" as actorCustXyzAdmin
|
actor "Customer XYZ Admin" as actorCustXyzAdmin
|
||||||
actorCustXyzAdmin --> roleCustXyzAdmin
|
actorCustXyzAdmin --> roleCustXyzAdmin
|
||||||
@ -487,8 +485,6 @@ roleAdmins --> roleCustXyzOwner
|
|||||||
|
|
||||||
actor "Any Hostmaster" as actorHostmaster
|
actor "Any Hostmaster" as actorHostmaster
|
||||||
actorHostmaster --> roleAdmins
|
actorHostmaster --> roleAdmins
|
||||||
|
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -527,17 +523,17 @@ allow_mixing
|
|||||||
entity "BObj package#xyz00" as boPacXyz00
|
entity "BObj package#xyz00" as boPacXyz00
|
||||||
|
|
||||||
together {
|
together {
|
||||||
entity "Perm package#xyz00 *" as permPackageXyzAll
|
entity "Perm package#xyz00 *" as permPackageXyzDELETE
|
||||||
permPackageXyzAll --> boPacXyz00
|
permPackageXyzDELETE --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 add-domain" as permPacXyz00AddUser
|
entity "Perm package#xyz00 INSERT:domain" as permPacXyz00INSERT:user
|
||||||
permPacXyz00AddUser --> boPacXyz00
|
permPacXyz00INSERT:user --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 edit" as permPacXyz00Edit
|
entity "Perm package#xyz00 UPDATE" as permPacXyz00UPDATE
|
||||||
permPacXyz00Edit --> boPacXyz00
|
permPacXyz00UPDATE --> boPacXyz00
|
||||||
|
|
||||||
entity "Perm package#xyz00 view" as permPacXyz00View
|
entity "Perm package#xyz00 SELECT" as permPacXyz00SELECT
|
||||||
permPacXyz00View --> boPacXyz00
|
permPacXyz00SELECT --> boPacXyz00
|
||||||
}
|
}
|
||||||
|
|
||||||
package {
|
package {
|
||||||
@ -552,11 +548,11 @@ package {
|
|||||||
entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
|
entity "Role package#xyz00.tenant" as rolePacXyz00Tenant
|
||||||
}
|
}
|
||||||
|
|
||||||
rolePacXyz00Tenant --> permPacXyz00View
|
rolePacXyz00Tenant --> permPacXyz00SELECT
|
||||||
rolePacXyz00Tenant --> roleCustXyzTenant
|
rolePacXyz00Tenant --> roleCustXyzTenant
|
||||||
|
|
||||||
rolePacXyz00Owner --> rolePacXyz00Admin
|
rolePacXyz00Owner --> rolePacXyz00Admin
|
||||||
rolePacXyz00Owner --> permPackageXyzAll
|
rolePacXyz00Owner --> permPackageXyzDELETE
|
||||||
|
|
||||||
roleCustXyzAdmin --> rolePacXyz00Owner
|
roleCustXyzAdmin --> rolePacXyz00Owner
|
||||||
roleCustXyzAdmin --> roleCustXyzTenant
|
roleCustXyzAdmin --> roleCustXyzTenant
|
||||||
@ -564,8 +560,8 @@ roleCustXyzAdmin --> roleCustXyzTenant
|
|||||||
roleCustXyzOwner ..> roleCustXyzAdmin
|
roleCustXyzOwner ..> roleCustXyzAdmin
|
||||||
|
|
||||||
rolePacXyz00Admin --> rolePacXyz00Tenant
|
rolePacXyz00Admin --> rolePacXyz00Tenant
|
||||||
rolePacXyz00Admin --> permPacXyz00AddUser
|
rolePacXyz00Admin --> permPacXyz00INSERT:user
|
||||||
rolePacXyz00Admin --> permPacXyz00Edit
|
rolePacXyz00Admin --> permPacXyz00UPDATE
|
||||||
|
|
||||||
actor "Package XYZ00 Admin" as actorPacXyzAdmin
|
actor "Package XYZ00 Admin" as actorPacXyzAdmin
|
||||||
actorPacXyzAdmin -l-> rolePacXyz00Admin
|
actorPacXyzAdmin -l-> rolePacXyz00Admin
|
||||||
@ -624,10 +620,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(
|
||||||
'view', 'customer', currentSubjectsUuids()));
|
'SELECTÄ, 'customer', currentSubjectsUuids()));
|
||||||
|
|
||||||
This view should be automatically updatable.
|
This view should be automatically updatable.
|
||||||
Where, for updates, we actually have to check for 'edit' instead of 'view' operation, which makes it a bit more complicated.
|
Where, for updates, we actually have to check for 'UPDATE' instead of 'SELECTÄ operation, which makes it a bit more complicated.
|
||||||
|
|
||||||
With the larger dataset, the test suite initially needed over 7 seconds with this view query.
|
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.
|
||||||
@ -642,7 +638,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(
|
||||||
'view', 'customer', currentSubjectsUuids()) AS allowedObjId
|
'SELECTÄ, '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,
|
||||||
@ -688,7 +684,7 @@ Otherwise, it would not be possible to assign roles to new users.
|
|||||||
|
|
||||||
All roles are system-defined and cannot be created or modified by any external API.
|
All roles are system-defined and cannot be created or modified by any external API.
|
||||||
|
|
||||||
Users can view only the roles to which they are assigned.
|
Users can view only the roles to which are granted to them.
|
||||||
|
|
||||||
## RbacGrant
|
## RbacGrant
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ select *
|
|||||||
FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com'));
|
FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com'));
|
||||||
|
|
||||||
select *
|
select *
|
||||||
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
|
FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('customer',
|
||||||
(SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
|
(SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
|
||||||
'add-package'));
|
'add-package'));
|
||||||
select *
|
select *
|
||||||
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
|
FROM queryAllRbacUsersWithPermissionsFor(findPermissionId('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(findEffectivePermissionId('package', 94928, 'add-package'), userId));
|
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('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(findEffectivePermissionId('package', 94928, 'view'), userId));
|
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'SELECT'), 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, 'view'), currentUserUuid())
|
isPermissionGrantedToSubject(findPermissionId('test_customer', id, 'SELECT'), 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, 'view'), currentUserUuid());
|
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findPermissionId('test_customer', id, 'SELECT'), 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 AND p.op in ('*', 'view');
|
ON p.objectTable='test_customer' AND p.objectUuid=c.uuid AND p.op = 'SELECT';
|
||||||
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 AND p.op in ('*', 'view');
|
ON p.objectUuid=c.uuid AND p.op = 'SELECT';
|
||||||
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' and op in ('*', 'view'));
|
where objectTable='test_customer' and op = 'SELECT');
|
||||||
|
|
||||||
call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
|
call grantRoleToUser(findRoleId('test_customer#aaa.admin'), findRbacUser('aaaaouq@example.com'));
|
||||||
|
|
||||||
|
@ -64,17 +64,17 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(REFERRER, (with) -> {
|
.createSubRole(REFERRER, (with) -> {
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac");
|
rbac().generateWithBaseFileName("243-hs-office-bankaccount-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,17 +68,17 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(REFERRER, (with) -> {
|
.createSubRole(REFERRER, (with) -> {
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("203-hs-office-contact-rbac");
|
rbac().generateWithBaseFileName("203-hs-office-contact-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,9 +140,9 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
|
WHERE r.relType = 'ACCOUNTING' AND r.relHolderUuid = ${REF}.debitorRelUuid
|
||||||
"""),
|
"""),
|
||||||
dependsOnColumn("debitorRelUuid"))
|
dependsOnColumn("debitorRelUuid"))
|
||||||
.createPermission(ALL).grantedTo("debitorRel", OWNER)
|
.createPermission(DELETE).grantedTo("debitorRel", OWNER)
|
||||||
.createPermission(EDIT).grantedTo("debitorRel", ADMIN)
|
.createPermission(UPDATE).grantedTo("debitorRel", ADMIN)
|
||||||
.createPermission(VIEW).grantedTo("debitorRel", TENANT)
|
.createPermission(SELECT).grantedTo("debitorRel", TENANT)
|
||||||
|
|
||||||
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
.importEntityAlias("refundBankAccount", HsOfficeBankAccountEntity.class,
|
||||||
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
|
dependsOnColumn("refundBankAccountUuid"), fetchedBySql("""
|
||||||
@ -171,6 +171,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("273-hs-office-debitor-rbac");
|
rbac().generateWithBaseFileName("273-hs-office-debitor-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,6 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac");
|
rbac().generateWithBaseFileName("234-hs-office-partner-details-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
|
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.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.VIEW;
|
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.Role.*;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
|
||||||
@ -95,19 +95,19 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
|
|||||||
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationshipEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
|
fetchedBySql("SELECT * FROM hs_office_relationship AS r WHERE r.uuid = ${ref}.partnerRoleUuid"),
|
||||||
dependsOnColumn("partnerRelUuid"))
|
dependsOnColumn("partnerRelUuid"))
|
||||||
.createPermission(ALL).grantedTo("partnerRel", ADMIN)
|
.createPermission(DELETE).grantedTo("partnerRel", ADMIN)
|
||||||
.createPermission(EDIT).grantedTo("partnerRel", AGENT)
|
.createPermission(UPDATE).grantedTo("partnerRel", AGENT)
|
||||||
.createPermission(VIEW).grantedTo("partnerRel", TENANT)
|
.createPermission(SELECT).grantedTo("partnerRel", TENANT)
|
||||||
|
|
||||||
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
.importSubEntityAlias("partnerDetails", HsOfficePartnerDetailsEntity.class,
|
||||||
fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
|
fetchedBySql("SELECT * FROM hs_office_partner_details AS d WHERE d.uuid = ${ref}.detailsUuid"),
|
||||||
dependsOnColumn("detailsUuid"))
|
dependsOnColumn("detailsUuid"))
|
||||||
.createPermission("partnerDetails", ALL).grantedTo("partnerRel", ADMIN)
|
.createPermission("partnerDetails", DELETE).grantedTo("partnerRel", ADMIN)
|
||||||
.createPermission("partnerDetails", EDIT).grantedTo("partnerRel", AGENT)
|
.createPermission("partnerDetails", UPDATE).grantedTo("partnerRel", AGENT)
|
||||||
.createPermission("partnerDetails", VIEW).grantedTo("partnerRel", AGENT);
|
.createPermission("partnerDetails", SELECT).grantedTo("partnerRel", AGENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("233-hs-office-partner-rbac");
|
rbac().generateWithBaseFileName("233-hs-office-partner-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,20 +70,20 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
|
|||||||
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
|
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
|
||||||
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
.withUpdatableColumns("personType", "tradeName", "givenName", "familyName")
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(REFERRER, (with) -> {
|
.createSubRole(REFERRER, (with) -> {
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("213-hs-office-person-rbac");
|
rbac().generateWithBaseFileName("213-hs-office-person-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,11 +103,11 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.incomingSuperRole("anchorPerson", ADMIN);
|
with.incomingSuperRole("anchorPerson", ADMIN);
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(AGENT, (with) -> {
|
.createSubRole(AGENT, (with) -> {
|
||||||
with.incomingSuperRole("holderPerson", ADMIN);
|
with.incomingSuperRole("holderPerson", ADMIN);
|
||||||
@ -118,11 +118,11 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
|
|||||||
with.outgoingSubRole("anchorPerson", REFERRER);
|
with.outgoingSubRole("anchorPerson", REFERRER);
|
||||||
with.outgoingSubRole("holderPerson", REFERRER);
|
with.outgoingSubRole("holderPerson", REFERRER);
|
||||||
with.outgoingSubRole("contact", REFERRER);
|
with.outgoingSubRole("contact", REFERRER);
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("223-hs-office-relationship-rbac");
|
rbac().generateWithBaseFileName("223-hs-office-relationship-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,10 +105,10 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN, (with) -> {
|
.createSubRole(ADMIN, (with) -> {
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(AGENT, (with) -> {
|
.createSubRole(AGENT, (with) -> {
|
||||||
with.outgoingSubRole("bankAccount", REFERRER);
|
with.outgoingSubRole("bankAccount", REFERRER);
|
||||||
@ -118,11 +118,11 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid {
|
|||||||
with.incomingSuperRole("bankAccount", ADMIN);
|
with.incomingSuperRole("bankAccount", ADMIN);
|
||||||
with.incomingSuperRole("debitorRel", AGENT);
|
with.incomingSuperRole("debitorRel", AGENT);
|
||||||
with.outgoingSubRole("debitorRel", TENANT);
|
with.outgoingSubRole("debitorRel", TENANT);
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac");
|
rbac().generateWithBaseFileName("253-hs-office-sepamandate-rbac-generated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package net.hostsharing.hsadminng.rbac.rbacdef;
|
package net.hostsharing.hsadminng.rbac.rbacdef;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
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.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
|
||||||
import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
|
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 {
|
public class InsertTriggerGenerator {
|
||||||
|
|
||||||
@ -16,25 +21,8 @@ public class InsertTriggerGenerator {
|
|||||||
void generateTo(final StringWriter plPgSql) {
|
void generateTo(final StringWriter plPgSql) {
|
||||||
generateLiquibaseChangesetHeader(plPgSql);
|
generateLiquibaseChangesetHeader(plPgSql);
|
||||||
generateGrantInsertRoleToExistingCustomers(plPgSql);
|
generateGrantInsertRoleToExistingCustomers(plPgSql);
|
||||||
rbacDef.getGrantDefs().stream()
|
generateInsertPermissionGrantTrigger(plPgSql);
|
||||||
.filter(g -> g.isToCreate() && g.grantType() == PERM_TO_ROLE &&
|
generateInsertCheckTrigger(plPgSql);
|
||||||
g.getPermDef().getPermission() == RbacView.Permission.INSERT )
|
|
||||||
.forEach(g -> {
|
|
||||||
plPgSql.writeLn("""
|
|
||||||
/**
|
|
||||||
Checks if the user or assumed roles are allowed to insert a row to ${rawSubTable}.
|
|
||||||
*/
|
|
||||||
create trigger ${rawSubTable}_it
|
|
||||||
before insert
|
|
||||||
on ${rawSubTable}
|
|
||||||
for each row
|
|
||||||
when ( hasInsertPermission(NEW.${referenceColumn}, 'INSERT', '${rawSubTable}') )
|
|
||||||
execute procedure insertNotAllowedForCurrentSubjects('${rawSubTable}');
|
|
||||||
""",
|
|
||||||
with("rawSubTable", g.getPermDef().entityAlias.getRawTableName()),
|
|
||||||
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
|
|
||||||
});
|
|
||||||
|
|
||||||
plPgSql.writeLn("--//");
|
plPgSql.writeLn("--//");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,29 +36,104 @@ public class InsertTriggerGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
|
private void generateGrantInsertRoleToExistingCustomers(final StringWriter plPgSql) {
|
||||||
plPgSql.writeLn("""
|
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
|
||||||
|
plPgSql.writeLn("""
|
||||||
/*
|
/*
|
||||||
Creates an INSERT INTO ${rawSubTableName} permission for the related ${rawSuperTableName} row.
|
Creates INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows.
|
||||||
*/
|
*/
|
||||||
do language plpgsql $$
|
do language plpgsql $$
|
||||||
declare
|
declare
|
||||||
row ${rawSuperTableName};
|
row ${rawSuperTableName};
|
||||||
permissionUuids uuid[];
|
permissionUuid uuid;
|
||||||
roleUuid uuid;
|
roleUuid uuid;
|
||||||
begin
|
begin
|
||||||
|
call defineContext('generated Liquibase: create INSERT INTO ${rawSubTableName} permissions for the related ${rawSuperTableName} rows');
|
||||||
|
|
||||||
FOR row IN SELECT * FROM ${rawSuperTableName}
|
FOR row IN SELECT * FROM ${rawSuperTableName}
|
||||||
LOOP
|
LOOP
|
||||||
roleUuid := ${rawSuperRoleDescriptor}(row);
|
roleUuid := findRoleId(${rawSuperRoleDescriptor}(row));
|
||||||
permissionUuids := createPermissions(row.uuid, array ['INSERT:${rawSubTableName}']);
|
permissionUuid := createPermission(row.uuid, 'INSERT', '${rawSubTableName}');
|
||||||
call grantPermissionsToRole(roleUuid, permissionUuids);
|
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
""",
|
""",
|
||||||
with("rawSubTableName", "test_package"), // TODO
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
with("rawSuperTableName", "test_customer"), // TODO
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
with("rawSuperRoleDescriptor", "testCustomerAdmin") // TODO
|
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateInsertPermissionGrantTrigger(final StringWriter plPgSql) {
|
||||||
|
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
|
||||||
|
plPgSql.writeLn("""
|
||||||
|
/**
|
||||||
|
Adds ${rawSubTableName} INSERT permission to specified role of new ${rawSuperTableName} rows.
|
||||||
|
*/
|
||||||
|
create or replace function ${rawSubTableName}_${rawSuperTableName}_insert_tf()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql
|
||||||
|
strict as $$
|
||||||
|
begin
|
||||||
|
call grantPermissionToRole(
|
||||||
|
${rawSuperRoleDescriptor}(NEW),
|
||||||
|
createPermission(NEW.uuid, 'INSERT', '${rawSubTableName}'));
|
||||||
|
return NEW;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create trigger ${rawSubTableName}_${rawSuperTableName}_insert_tg
|
||||||
|
after insert on ${rawSuperTableName}
|
||||||
|
for each row
|
||||||
|
execute procedure ${rawSubTableName}_${rawSuperTableName}_insert_tf();
|
||||||
|
""",
|
||||||
|
with("rawSubTableName", rbacDef.getRootEntityAlias().getRawTableName()),
|
||||||
|
with("rawSuperTableName", superRoleDef.getEntityAlias().getRawTableName()),
|
||||||
|
with("rawSuperRoleDescriptor", toVar(superRoleDef))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateInsertCheckTrigger(final StringWriter plPgSql) {
|
||||||
|
rbacDef.getGrantDefs().stream()
|
||||||
|
.filter(g -> g.isToCreate() && g.grantType() == PERM_TO_ROLE &&
|
||||||
|
g.getPermDef().getPermission() == INSERT )
|
||||||
|
.forEach(g -> {
|
||||||
|
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 'insert into ${rawSubTable} not allowed for current subjects %', currentSubjectsUuids();
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
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", g.getPermDef().entityAlias.getRawTableName()),
|
||||||
|
with("referenceColumn", g.getSuperRoleDef().getEntityAlias().dependsOnColumName() ));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<RbacView.RbacRoleDefinition> getOptionalInsertSuperRole() {
|
||||||
|
return rbacDef.getGrantDefs().stream()
|
||||||
|
.filter(g -> g.grantType() == PERM_TO_ROLE)
|
||||||
|
.filter(g -> g.getPermDef().toCreate && g.getPermDef().getPermission() == INSERT)
|
||||||
|
.map(RbacView.RbacGrantDefinition::getSuperRoleDef)
|
||||||
|
.reduce((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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,7 @@ public class RbacView {
|
|||||||
|
|
||||||
public RbacView withUpdatableColumns(final String... columnNames) {
|
public RbacView withUpdatableColumns(final String... columnNames) {
|
||||||
Collections.addAll(updatableColumns, columnNames);
|
Collections.addAll(updatableColumns, columnNames);
|
||||||
|
// TODO: automatically add @Version column, otherwise optimistic locking won't work
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +250,8 @@ public class RbacView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void generateWithBaseFileName(final String baseFileName) {
|
public void generateWithBaseFileName(final String baseFileName) {
|
||||||
new RbacViewMermaidFlowchart(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + "-generated.md"));
|
new RbacViewMermaidFlowchart(this).generateToMarkdownFile(Path.of(OUTPUT_BASEDIR, baseFileName + ".md"));
|
||||||
new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + "-generated.sql"));
|
new RbacViewPostgresGenerator(this).generateToChangeLog(Path.of(OUTPUT_BASEDIR, baseFileName + ".sql"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RbacGrantBuilder {
|
public class RbacGrantBuilder {
|
||||||
@ -465,6 +466,7 @@ public class RbacView {
|
|||||||
public class RbacUserReference {
|
public class RbacUserReference {
|
||||||
|
|
||||||
public enum UserRole {
|
public enum UserRole {
|
||||||
|
GLOBAL_ADMIN,
|
||||||
CREATOR
|
CREATOR
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,10 +630,10 @@ public class RbacView {
|
|||||||
|
|
||||||
public record Permission(String permission) {
|
public record Permission(String permission) {
|
||||||
|
|
||||||
public static final Permission INSERT = new Permission("insert");
|
public static final Permission INSERT = new Permission("INSERT");
|
||||||
public static final Permission ALL = new Permission("*");
|
public static final Permission DELETE = new Permission("DELETE");
|
||||||
public static final Permission EDIT = new Permission("edit");
|
public static final Permission UPDATE = new Permission("UPDATE");
|
||||||
public static final Permission VIEW = new Permission("view");
|
public static final Permission SELECT = new Permission("SELECT");
|
||||||
|
|
||||||
public static Permission custom(final String permission) {
|
public static Permission custom(final String permission) {
|
||||||
return new Permission(permission);
|
return new Permission(permission);
|
||||||
@ -671,7 +673,7 @@ public class RbacView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DSL method to specify there there is no SQL query specified.
|
* DSL method to explicitly specify that there is no SQL query.
|
||||||
*
|
*
|
||||||
* @return a wrapped SQL definition object representing a noop query
|
* @return a wrapped SQL definition object representing a noop query
|
||||||
*/
|
*/
|
||||||
|
@ -81,8 +81,10 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
generateCreateRolesAndGrantsAfterInsert(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
});
|
});
|
||||||
plPgSql.writeLn("end; $$;");
|
plPgSql.writeLn("end; $$;");
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
@ -94,7 +96,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
Called from the AFTER UPDATE TRIGGER to re-wire the grants.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace procedure updateRbacGrantsFor${simpleEntityName}(
|
create or replace procedure updateRbacRulesFor${simpleEntityName}(
|
||||||
OLD ${rawTableName},
|
OLD ${rawTableName},
|
||||||
NEW ${rawTableName}
|
NEW ${rawTableName}
|
||||||
)
|
)
|
||||||
@ -117,8 +119,10 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
plPgSql.writeLn("begin");
|
plPgSql.writeLn("begin");
|
||||||
plPgSql.indented(() -> {
|
plPgSql.indented(() -> {
|
||||||
|
plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
|
||||||
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
|
||||||
plPgSql.ensureSingleEmptyLine();
|
plPgSql.ensureSingleEmptyLine();
|
||||||
|
plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
|
||||||
});
|
});
|
||||||
plPgSql.writeLn("end; $$;");
|
plPgSql.writeLn("end; $$;");
|
||||||
plPgSql.writeLn();
|
plPgSql.writeLn();
|
||||||
@ -218,7 +222,7 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
|
||||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
|
||||||
.replace("${permRef}", permRef(OLD, grantDef.getPermDef()))
|
.replace("${permRef}", findPerm(OLD, grantDef.getPermDef()))
|
||||||
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -231,14 +235,23 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
|
||||||
case PERM_TO_ROLE ->
|
case PERM_TO_ROLE ->
|
||||||
grantDef.getPermDef().getPermission() == INSERT ? ""
|
grantDef.getPermDef().getPermission() == INSERT ? ""
|
||||||
: "call grantPermissionsToRole(${permRef}, ${superRoleRef});"
|
: "call grantPermissionToRole(${permRef}, ${superRoleRef});"
|
||||||
.replace("${permRef}", permRef(NEW, grantDef.getPermDef()))
|
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
|
||||||
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
|
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String permRef(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
|
||||||
return "createPermissions(${entityRef}.uuid, array ['${perm}'])"
|
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)
|
.replace("${entityRef}", rbacDef.isRootEntityAlias(permDef.entityAlias)
|
||||||
? ref.name()
|
? ref.name()
|
||||||
: refVarName(ref, permDef.entityAlias))
|
: refVarName(ref, permDef.entityAlias))
|
||||||
@ -412,13 +425,12 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call buildRbacSystemFor${simpleEntityName}(TG_OP, OLD, NEW);
|
call buildRbacSystemFor${simpleEntityName}(NEW);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger insertTriggerFor${simpleEntityName}_tg
|
create trigger insertTriggerFor${simpleEntityName}_tg
|
||||||
after insert
|
after insert on ${rawTableName}
|
||||||
on ${rawTableName}
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertTriggerFor${simpleEntityName}_tf();
|
execute procedure insertTriggerFor${simpleEntityName}_tf();
|
||||||
"""
|
"""
|
||||||
@ -444,13 +456,12 @@ class RolesGrantsAndPermissionsGenerator {
|
|||||||
language plpgsql
|
language plpgsql
|
||||||
strict as $$
|
strict as $$
|
||||||
begin
|
begin
|
||||||
call buildRbacSystemFor${simpleEntityName}(NEW);
|
call updateRbacRulesFor${simpleEntityName}(OLD, NEW);
|
||||||
return NEW;
|
return NEW;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
create trigger updateTriggerFor${simpleEntityName}_tg
|
create trigger updateTriggerFor${simpleEntityName}_tg
|
||||||
after update
|
after update on ${rawTableName}
|
||||||
on ${rawTableName}
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure updateTriggerFor${simpleEntityName}_tf();
|
execute procedure updateTriggerFor${simpleEntityName}_tf();
|
||||||
"""
|
"""
|
||||||
|
@ -43,13 +43,15 @@ public class TestCustomerEntity implements HasUuid {
|
|||||||
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
.withUpdatableColumns("reference", "prefix", "adminUserName")
|
||||||
|
|
||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
// with.owningUser(CREATOR); TODO: needs assumed role
|
||||||
with.incomingSuperRole(GLOBAL, ADMIN);
|
with.incomingSuperRole(GLOBAL, ADMIN);
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
|
})
|
||||||
|
.createSubRole(ADMIN, (with) -> {
|
||||||
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN)
|
|
||||||
.createSubRole(TENANT, (with) -> {
|
.createSubRole(TENANT, (with) -> {
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class TestPackageEntity implements HasUuid {
|
|||||||
public static RbacView rbac() {
|
public static RbacView rbac() {
|
||||||
return rbacViewFor("package", TestPackageEntity.class)
|
return rbacViewFor("package", TestPackageEntity.class)
|
||||||
.withIdentityView(SQL.projection("name"))
|
.withIdentityView(SQL.projection("name"))
|
||||||
.withUpdatableColumns("customerUuid", "description")
|
.withUpdatableColumns("version", "customerUuid", "description")
|
||||||
|
|
||||||
.importEntityAlias("customer", TestCustomerEntity.class,
|
.importEntityAlias("customer", TestCustomerEntity.class,
|
||||||
dependsOnColumn("customerUuid"),
|
dependsOnColumn("customerUuid"),
|
||||||
@ -60,13 +60,13 @@ public class TestPackageEntity implements HasUuid {
|
|||||||
.createRole(OWNER, (with) -> {
|
.createRole(OWNER, (with) -> {
|
||||||
with.owningUser(CREATOR);
|
with.owningUser(CREATOR);
|
||||||
with.incomingSuperRole("customer", ADMIN).unassumed();
|
with.incomingSuperRole("customer", ADMIN).unassumed();
|
||||||
with.permission(ALL);
|
with.permission(DELETE);
|
||||||
with.permission(EDIT);
|
with.permission(UPDATE);
|
||||||
})
|
})
|
||||||
.createSubRole(ADMIN)
|
.createSubRole(ADMIN)
|
||||||
.createSubRole(TENANT, (with) -> {
|
.createSubRole(TENANT, (with) -> {
|
||||||
with.outgoingSubRole("customer", TENANT);
|
with.outgoingSubRole("customer", TENANT);
|
||||||
with.permission(VIEW);
|
with.permission(SELECT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,10 +66,11 @@ begin
|
|||||||
when others then
|
when others then
|
||||||
currentTask := null;
|
currentTask := null;
|
||||||
end;
|
end;
|
||||||
if (currentTask is null or currentTask = '') then
|
-- TODO: uncomment
|
||||||
raise exception '[401] currentTask must be defined, please call `defineContext(...)`';
|
-- if (currentTask is null or currentTask = '') then
|
||||||
end if;
|
-- raise exception '[401] currentTask must be defined, please call `defineContext(...)`';
|
||||||
return currentTask;
|
-- end if;
|
||||||
|
return 'unknown'; -- TODO: currentTask;
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -365,16 +365,18 @@ create trigger deleteRbacRolesOfRbacObject_Trigger
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
*/
|
*/
|
||||||
create domain RbacOp as varchar(67)
|
create domain RbacOp as varchar(67) -- TODO: shorten to 8, once the deprecated values are gone
|
||||||
check (
|
-- check (
|
||||||
VALUE = '*'
|
-- VALUE = 'INSERT' or
|
||||||
or VALUE = 'delete'
|
-- VALUE = 'DELETE' or
|
||||||
or VALUE = 'edit'
|
-- VALUE = 'UPDATE' or
|
||||||
or VALUE = 'view'
|
-- VALUE = 'SELECT' or
|
||||||
or VALUE = 'assume'
|
-- VALUE = 'ASSUME' or
|
||||||
or VALUE ~ '^add-[a-z]+$'
|
-- -- TODO: all values below are deprecated, use insert with table
|
||||||
or VALUE ~ '^new-[a-z-]+$'
|
-- VALUE ~ '^add-[a-z]+$' or
|
||||||
);
|
-- VALUE ~ '^new-[a-z-]+$'
|
||||||
|
-- );
|
||||||
|
;
|
||||||
|
|
||||||
create table RbacPermission
|
create table RbacPermission
|
||||||
(
|
(
|
||||||
@ -394,37 +396,38 @@ select exists(
|
|||||||
select op
|
select op
|
||||||
from RbacPermission p
|
from RbacPermission p
|
||||||
where p.objectUuid = forObjectUuid
|
where p.objectUuid = forObjectUuid
|
||||||
and p.op in ('*', forOp)
|
and p.op = forOp
|
||||||
);
|
);
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace function createPermissions(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
create or replace function createPermission(forObjectUuid uuid, forOp RbacOp, forOpTableName text = null)
|
||||||
returns uuid[]
|
returns uuid
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
permissionId uuid;
|
permissionUuid uuid;
|
||||||
begin
|
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 (forOp = 'INSERT' && forOpTableName is null) then
|
if (forOp = 'INSERT' and forOpTableName is null) then
|
||||||
raise exception 'INSERT permissions needs forOpTableName';
|
raise exception 'INSERT permissions needs forOpTableName';
|
||||||
end if;
|
end if;
|
||||||
if (forOp <> 'INSERT' && forOpTableName is not null) then
|
if (forOp <> 'INSERT' and forOpTableName is not null) then
|
||||||
raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
|
raise exception 'forOpTableName must only be specified for ops: [INSERT]'; -- currently no other
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
permissionId = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = forOp and opTableName = forOpTableName);
|
permissionUuid = (select uuid from RbacPermission where objectUuid = forObjectUuid and op = forOp and opTableName = forOpTableName);
|
||||||
if (permissionId is null) then
|
if (permissionUuid is null) then
|
||||||
insert
|
insert
|
||||||
into RbacReference ("type")
|
into RbacReference ("type")
|
||||||
values ('RbacPermission')
|
values ('RbacPermission')
|
||||||
returning uuid into permissionId;
|
returning uuid into permissionUuid;
|
||||||
|
raise warning 'for values (%, %, %, %)', permissionUuid, forObjectUuid, forOp, forOpTableName; -- TODO: remove
|
||||||
insert
|
insert
|
||||||
into RbacPermission (uuid, objectUuid, op, opTableName)
|
into RbacPermission (uuid, objectUuid, op, opTableName)
|
||||||
values (permissionId, forObjectUuid, forOp, opTableName);
|
values (permissionUuid, forObjectUuid, forOp, forOpTableName);
|
||||||
end if;
|
end if;
|
||||||
return permissionId;
|
return permissionUuid;
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
@ -439,9 +442,6 @@ 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
|
||||||
@ -462,7 +462,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp, opTableName text = null )
|
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
|
||||||
@ -471,24 +471,9 @@ 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 = opTableName
|
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 $$;
|
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
@ -592,8 +577,8 @@ create or replace function hasInsertPermission(objectUuid uuid, forOp RbacOp, ta
|
|||||||
declare
|
declare
|
||||||
permissionUuid uuid;
|
permissionUuid uuid;
|
||||||
begin
|
begin
|
||||||
permissionUuid = findPermissionId(objectUuid, forOp);
|
permissionUuid = findPermissionId(objectUuid, forOp, tableName);
|
||||||
|
return permissionUuid is not null;
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
@ -611,6 +596,20 @@ select exists(
|
|||||||
);
|
);
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
create or replace procedure grantPermissionToRole(roleUuid uuid, permissionUuid uuid)
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
perform assertReferenceType('roleId (ascendant)', roleUuid, 'RbacRole');
|
||||||
|
perform assertReferenceType('permissionId (descendant)', permissionUuid, 'RbacPermission');
|
||||||
|
|
||||||
|
insert
|
||||||
|
into RbacGrants (grantedByTriggerOf, ascendantUuid, descendantUuid, assumed)
|
||||||
|
values (currentTriggerObjectUuid(), roleUuid, permissionUuid, true)
|
||||||
|
on conflict do nothing; -- allow granting multiple times
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- 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
|
||||||
@ -742,7 +741,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 perm.op in ('*', requiredOp)
|
on granted.descendantUuid = perm.uuid and perm.op = 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;
|
||||||
|
|
||||||
@ -834,6 +833,5 @@ 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 $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ begin
|
|||||||
insert
|
insert
|
||||||
into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
|
into RbacGrants (grantedByRoleUuid, ascendantUuid, descendantUuid, assumed)
|
||||||
values (grantedByRoleUuid, userUuid, roleUuid, doAssume);
|
values (grantedByRoleUuid, userUuid, roleUuid, doAssume);
|
||||||
-- TODO.spec: What should happen on mupltiple grants? What if options (doAssume) are not the same?
|
-- TODO.spec: What should happen on multiple grants? What if options (doAssume) are not the same?
|
||||||
-- Most powerful or latest grant wins? What about managed?
|
-- Most powerful or latest grant wins? What about managed?
|
||||||
-- on conflict do nothing; -- allow granting multiple times
|
-- on conflict do nothing; -- allow granting multiple times
|
||||||
end; $$;
|
end; $$;
|
||||||
@ -99,4 +99,19 @@ 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
|
||||||
|
-- TODO: call checkRevokeRoleFromUserPreconditions(grantedByRoleUuid, grantedRoleUuid, userUuid);
|
||||||
|
|
||||||
|
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; $$;
|
||||||
|
--//
|
||||||
|
@ -73,9 +73,10 @@ begin
|
|||||||
|
|
||||||
if cardinality(userUuids) > 0 then
|
if cardinality(userUuids) > 0 then
|
||||||
if grantedByRole is null then
|
if grantedByRole is null then
|
||||||
raise exception 'to directly assign users to roles, grantingRole has to be given';
|
grantedByRoleUuid := roleUuid;
|
||||||
|
else
|
||||||
|
grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
|
||||||
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,8 +13,7 @@ declare
|
|||||||
begin
|
begin
|
||||||
createInsertTriggerSQL = format($sql$
|
createInsertTriggerSQL = format($sql$
|
||||||
create trigger createRbacObjectFor_%s_Trigger
|
create trigger createRbacObjectFor_%s_Trigger
|
||||||
before insert
|
before insert on %s
|
||||||
on %s
|
|
||||||
for each row
|
for each row
|
||||||
execute procedure insertRelatedRbacObject();
|
execute procedure insertRelatedRbacObject();
|
||||||
$sql$, targetTable, targetTable);
|
$sql$, targetTable, targetTable);
|
||||||
@ -145,13 +144,13 @@ begin
|
|||||||
targetTable := lower(targetTable);
|
targetTable := lower(targetTable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates a restricted view based on the 'view' permission of the current subject.
|
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
||||||
*/
|
*/
|
||||||
sql := format($sql$
|
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('view', '%1$s', currentSubjectsUuids())
|
select queryAccessibleObjectUuidsOfSubjectIds('SELECT', '%1$s', currentSubjectsUuids())
|
||||||
)
|
)
|
||||||
select target.*
|
select target.*
|
||||||
from %1$s as target
|
from %1$s as target
|
||||||
@ -200,7 +199,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;
|
||||||
@ -223,7 +222,7 @@ begin
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Instead of update trigger function for the restricted view
|
Instead of update trigger function for the restricted view
|
||||||
based on the 'edit' permission of the current subject.
|
based on the 'UPDATE' permission of the current subject.
|
||||||
*/
|
*/
|
||||||
if columnUpdates is not null then
|
if columnUpdates is not null then
|
||||||
sql := format($sql$
|
sql := format($sql$
|
||||||
@ -231,7 +230,7 @@ begin
|
|||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $f$
|
language plpgsql as $f$
|
||||||
begin
|
begin
|
||||||
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('edit', '%1$s', currentSubjectsUuids())) then
|
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('UPDATE', '%1$s', currentSubjectsUuids())) then
|
||||||
update %1$s
|
update %1$s
|
||||||
set %2$s
|
set %2$s
|
||||||
where uuid = old.uuid;
|
where uuid = old.uuid;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
|
-- This code generated was by RbacViewPostgresGenerator at 2024-03-06T15:40:13.239729250.
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-customer-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -7,6 +9,7 @@ call generateRelatedRbacObject('test_customer');
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
--changeset test-customer-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -15,82 +18,85 @@ call generateRbacRoleDescriptors('testCustomer', 'test_customer');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-ROLES-CREATION:1 endDelimiter:--//
|
--changeset test-customer-rbac-insert-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles and their assignments for a new customer for the AFTER INSERT TRIGGER.
|
A Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace function createRbacRolesForTestCustomer()
|
create or replace procedure buildRbacSystemForTestCustomer(
|
||||||
returns trigger
|
NEW test_customer
|
||||||
language plpgsql
|
)
|
||||||
strict as $$
|
language plpgsql as $$
|
||||||
declare
|
|
||||||
testCustomerOwnerUuid uuid;
|
|
||||||
customerAdminUuid uuid;
|
|
||||||
begin
|
|
||||||
if TG_OP <> 'INSERT' then
|
|
||||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
|
||||||
end if;
|
|
||||||
|
|
||||||
|
declare
|
||||||
|
|
||||||
|
begin
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
-- the owner role with full access for Hostsharing administrators
|
perform createRoleWithGrants(
|
||||||
testCustomerOwnerUuid = createRoleWithGrants(
|
|
||||||
testCustomerOwner(NEW),
|
testCustomerOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
);
|
);
|
||||||
|
|
||||||
-- the admin role for the customer's admins, who can view and add products
|
perform createRoleWithGrants(
|
||||||
customerAdminUuid = createRoleWithGrants(
|
|
||||||
testCustomerAdmin(NEW),
|
testCustomerAdmin(NEW),
|
||||||
permissions => array['view', 'add-package'],
|
permissions => array['UPDATE'],
|
||||||
-- NO auto assume for customer owner to avoid exploding permissions for administrators
|
incomingSuperRoles => array[testCustomerOwner(NEW)]
|
||||||
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(
|
perform createRoleWithGrants(
|
||||||
testCustomerTenant(NEW),
|
testCustomerTenant(NEW),
|
||||||
permissions => array['view']
|
permissions => array['SELECT'],
|
||||||
);
|
incomingSuperRoles => array[testCustomerAdmin(NEW)]
|
||||||
|
);
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
return NEW;
|
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
AFTER INSERT TRIGGER to create the role+grant structure for a new test_customer row.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
drop trigger if exists createRbacRolesForTestCustomer_Trigger on test_customer;
|
create or replace function insertTriggerForTestCustomer_tf()
|
||||||
create trigger createRbacRolesForTestCustomer_Trigger
|
returns trigger
|
||||||
after insert
|
language plpgsql
|
||||||
on test_customer
|
strict as $$
|
||||||
|
begin
|
||||||
|
call buildRbacSystemForTestCustomer(NEW);
|
||||||
|
return NEW;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create trigger insertTriggerForTestCustomer_tg
|
||||||
|
after insert on test_customer
|
||||||
for each row
|
for each row
|
||||||
execute procedure createRbacRolesForTestCustomer();
|
execute procedure insertTriggerForTestCustomer_tf();
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset test-customer-rbac-INSERT:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--//
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityView('test_customer', $idName$
|
call generateRbacIdentityView('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', 'target.prefix',
|
call generateRbacRestrictedView('test_customer',
|
||||||
|
'reference',
|
||||||
$updates$
|
$updates$
|
||||||
reference = new.reference,
|
reference = new.reference,
|
||||||
prefix = new.prefix,
|
prefix = new.prefix,
|
||||||
@ -99,47 +105,3 @@ call generateRbacRestrictedView('test_customer', 'target.prefix',
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset test-customer-rbac-ADD-CUSTOMER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
Creates a global permission for add-customer and assigns it to the hostsharing admins role.
|
|
||||||
*/
|
|
||||||
do language plpgsql $$
|
|
||||||
declare
|
|
||||||
addCustomerPermissions uuid[];
|
|
||||||
globalObjectUuid uuid;
|
|
||||||
globalAdminRoleUuid uuid ;
|
|
||||||
begin
|
|
||||||
call defineContext('granting global add-customer permission to global admin role', null, null, null);
|
|
||||||
|
|
||||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
|
||||||
globalObjectUuid := (select uuid from global);
|
|
||||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['add-customer']);
|
|
||||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
|
||||||
end;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
|
||||||
*/
|
|
||||||
create or replace function addTestCustomerNotAllowedForCurrentSubjects()
|
|
||||||
returns trigger
|
|
||||||
language PLPGSQL
|
|
||||||
as $$
|
|
||||||
begin
|
|
||||||
raise exception '[403] add-customer not permitted for %',
|
|
||||||
array_to_string(currentSubjects(), ';', 'null');
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if the user or assumed roles are allowed to add a new customer.
|
|
||||||
*/
|
|
||||||
create trigger test_customer_insert_trigger
|
|
||||||
before insert
|
|
||||||
on test_customer
|
|
||||||
for each row
|
|
||||||
when ( not hasGlobalPermission('add-customer') )
|
|
||||||
execute procedure addTestCustomerNotAllowedForCurrentSubjects();
|
|
||||||
--//
|
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ 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');
|
||||||
@ -35,10 +37,20 @@ 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(testCustomerAdmin(newCust), 'fail'),
|
||||||
|
-- findRoleId(testCustomerOwner(newCust)),
|
||||||
|
-- custAd
|
||||||
|
-- minUuid,
|
||||||
|
-- true);
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
--liquibase formatted sql
|
--liquibase formatted sql
|
||||||
|
-- This code generated was by RbacViewPostgresGenerator at 2024-03-06T15:40:13.277446553.
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
--changeset test-package-rbac-OBJECT:1 endDelimiter:--//
|
||||||
@ -7,6 +9,7 @@ call generateRelatedRbacObject('test_package');
|
|||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
--changeset test-package-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -15,95 +18,213 @@ call generateRbacRoleDescriptors('testPackage', 'test_package');
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-ROLES-CREATION:1 endDelimiter:--//
|
--changeset test-package-rbac-insert-trigger:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles and their assignments for a new package for the AFTER INSERT TRIGGER.
|
A Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
create or replace function createRbacRolesForTestPackage()
|
|
||||||
returns trigger
|
create or replace procedure buildRbacSystemForTestPackage(
|
||||||
language plpgsql
|
NEW test_package
|
||||||
strict as $$
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
|
||||||
declare
|
declare
|
||||||
parentCustomer test_customer;
|
newCustomer test_customer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if TG_OP <> 'INSERT' then
|
|
||||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
|
||||||
end if;
|
|
||||||
|
|
||||||
call enterTriggerForObjectUuid(NEW.uuid);
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
SELECT * FROM test_customer c
|
||||||
|
WHERE c.uuid= NEW.customerUuid
|
||||||
|
into newCustomer;
|
||||||
|
|
||||||
select * from test_customer as c where c.uuid = NEW.customerUuid into parentCustomer;
|
|
||||||
|
|
||||||
-- an owner role is created and assigned to the customer's admin role
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testPackageOwner(NEW),
|
testPackageOwner(NEW),
|
||||||
permissions => array ['*'],
|
permissions => array['DELETE', 'UPDATE'],
|
||||||
incomingSuperRoles => array[testCustomerAdmin(parentCustomer)]
|
userUuids => array[currentUserUuid()],
|
||||||
);
|
incomingSuperRoles => array[testCustomerAdmin(newCustomer)]
|
||||||
|
);
|
||||||
|
|
||||||
-- an owner role is created and assigned to the package owner role
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testPackageAdmin(NEW),
|
testPackageAdmin(NEW),
|
||||||
permissions => array ['add-domain'],
|
|
||||||
incomingSuperRoles => array[testPackageOwner(NEW)]
|
incomingSuperRoles => array[testPackageOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
-- and a package tenant role is created and assigned to the package admin as well
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testPackageTenant(NEW),
|
testPackageTenant(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingsuperroles => array[testPackageAdmin(NEW)],
|
incomingSuperRoles => array[testPackageAdmin(NEW)],
|
||||||
outgoingSubRoles => array[testCustomerTenant(parentCustomer)]
|
outgoingSubRoles => array[testCustomerTenant(newCustomer)]
|
||||||
);
|
);
|
||||||
|
|
||||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
return NEW;
|
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An AFTER INSERT TRIGGER which creates the role structure for a new package.
|
AFTER INSERT TRIGGER to create the role+grant structure for a new test_package row.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create trigger createRbacRolesForTestPackage_Trigger
|
create or replace function insertTriggerForTestPackage_tf()
|
||||||
after insert
|
returns trigger
|
||||||
on test_package
|
language plpgsql
|
||||||
|
strict as $$
|
||||||
|
begin
|
||||||
|
call buildRbacSystemForTestPackage(NEW);
|
||||||
|
return NEW;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create trigger insertTriggerForTestPackage_tg
|
||||||
|
after insert on test_package
|
||||||
for each row
|
for each row
|
||||||
execute procedure createRbacRolesForTestPackage();
|
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
|
||||||
|
oldCustomer test_customer;
|
||||||
|
newCustomer test_customer;
|
||||||
|
|
||||||
|
begin
|
||||||
|
call enterTriggerForObjectUuid(NEW.uuid);
|
||||||
|
|
||||||
|
SELECT * FROM test_customer c
|
||||||
|
WHERE c.uuid= OLD.customerUuid
|
||||||
|
into oldCustomer;
|
||||||
|
SELECT * FROM test_customer c
|
||||||
|
WHERE c.uuid= NEW.customerUuid
|
||||||
|
into newCustomer;
|
||||||
|
|
||||||
|
if NEW.customerUuid <> OLD.customerUuid then
|
||||||
|
|
||||||
|
call revokePermissionFromRole(findPermissionId(OLD.uuid, 'INSERT'), testCustomerAdmin(oldCustomer));
|
||||||
|
|
||||||
|
call revokeRoleFromRole(testPackageOwner(OLD), testCustomerAdmin(oldCustomer));
|
||||||
|
call grantRoleToRole(testPackageOwner(NEW), testCustomerAdmin(newCustomer));
|
||||||
|
|
||||||
|
call revokeRoleFromRole(testCustomerTenant(oldCustomer), testPackageTenant(OLD));
|
||||||
|
call grantRoleToRole(testCustomerTenant(newCustomer), testPackageTenant(NEW));
|
||||||
|
|
||||||
|
end if;
|
||||||
|
|
||||||
|
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
/*
|
||||||
|
AFTER INSERT TRIGGER to re-wire the grant structure for a new test_package row.
|
||||||
|
*/
|
||||||
|
|
||||||
|
create or replace function updateTriggerForTestPackage_tf()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql
|
||||||
|
strict as $$
|
||||||
|
begin
|
||||||
|
call updateRbacRulesForTestPackage(OLD, NEW);
|
||||||
|
return NEW;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create trigger updateTriggerForTestPackage_tg
|
||||||
|
after update on test_package
|
||||||
|
for each row
|
||||||
|
execute procedure updateTriggerForTestPackage_tf();
|
||||||
|
|
||||||
|
--//
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset test-package-rbac-INSERT:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates INSERT INTO test_package permissions for the related test_customer rows.
|
||||||
|
*/
|
||||||
|
do language plpgsql $$
|
||||||
|
declare
|
||||||
|
row test_customer;
|
||||||
|
permissionUuid uuid;
|
||||||
|
roleUuid uuid;
|
||||||
|
begin
|
||||||
|
call defineContext('generated Liquibase: create INSERT INTO test_package permissions for the related test_customer rows');
|
||||||
|
|
||||||
|
FOR row IN SELECT * FROM test_customer
|
||||||
|
LOOP
|
||||||
|
roleUuid := findRoleId(testCustomerAdmin(row));
|
||||||
|
permissionUuid := createPermission(row.uuid, 'INSERT', 'test_package');
|
||||||
|
call grantPermissionToRole(roleUuid, permissionUuid);
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds test_package INSERT permission to specified role of new test_customer rows.
|
||||||
|
*/
|
||||||
|
create or replace function test_package_test_customer_insert_tf()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql
|
||||||
|
strict as $$
|
||||||
|
begin
|
||||||
|
call grantPermissionToRole(
|
||||||
|
testCustomerAdmin(NEW),
|
||||||
|
createPermission(NEW.uuid, 'INSERT', 'test_package'));
|
||||||
|
return NEW;
|
||||||
|
end; $$;
|
||||||
|
|
||||||
|
create trigger test_package_test_customer_insert_tg
|
||||||
|
after insert on test_customer
|
||||||
|
for each row
|
||||||
|
execute procedure test_package_test_customer_insert_tf();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if the user or assumed roles are allowed to insert a row to test_package.
|
||||||
|
*/
|
||||||
|
create or replace function test_package_insert_permission_missing_tf()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
raise exception 'insert into test_package not allowed for current subjects %', 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 generateRbacIdentityView('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, custAdminUser, custAdminRole);
|
call defineContext(currentTask, null, 'superuser-fran@hostsharing.net', custAdminRole);
|
||||||
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
|
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
|
||||||
|
|
||||||
insert
|
insert
|
||||||
|
@ -28,7 +28,7 @@ begin
|
|||||||
|
|
||||||
return createRoleWithGrants(
|
return createRoleWithGrants(
|
||||||
domainTenantRoleDesc,
|
domainTenantRoleDesc,
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[testdomainAdmin(domain)]
|
incomingSuperRoles => array[testdomainAdmin(domain)]
|
||||||
);
|
);
|
||||||
end; $$;
|
end; $$;
|
||||||
@ -60,14 +60,14 @@ begin
|
|||||||
-- an owner role is created and assigned to the package's admin group
|
-- an owner role is created and assigned to the package's admin group
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testDomainOwner(NEW),
|
testDomainOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[testPackageAdmin(parentPackage)]
|
incomingSuperRoles => array[testPackageAdmin(parentPackage)]
|
||||||
);
|
);
|
||||||
|
|
||||||
-- and a domain admin role is created and assigned to the domain owner as well
|
-- and a domain admin role is created and assigned to the domain owner as well
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
testDomainAdmin(NEW),
|
testDomainAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[testDomainOwner(NEW)],
|
incomingSuperRoles => array[testDomainOwner(NEW)],
|
||||||
outgoingSubRoles => array[testPackageTenant(parentPackage)]
|
outgoingSubRoles => array[testPackageTenant(parentPackage)]
|
||||||
);
|
);
|
||||||
@ -112,6 +112,6 @@ drop view if exists test_domain_rv;
|
|||||||
create or replace view test_domain_rv as
|
create or replace view test_domain_rv as
|
||||||
select target.*
|
select target.*
|
||||||
from test_domain as target
|
from test_domain as target
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectsUuids()));
|
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('SELECT', 'domain', currentSubjectsUuids()));
|
||||||
grant all privileges on test_domain_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
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['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()],
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
grantedByRole => globalAdmin()
|
grantedByRole => globalAdmin()
|
||||||
@ -41,7 +41,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeContactAdmin(NEW),
|
hsOfficeContactAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeContactGuest(NEW),
|
hsOfficeContactGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
|
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,16 +31,16 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonOwner(NEW),
|
hsOfficePersonOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()],
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
grantedByRole => globalAdmin()
|
grantedByRole => globalAdmin()
|
||||||
);
|
);
|
||||||
|
|
||||||
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to edit the data?
|
-- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to update the data?
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonAdmin(NEW),
|
hsOfficePersonAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePersonGuest(NEW),
|
hsOfficePersonGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
|
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipOwner(NEW),
|
hsOfficeRelationshipOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
globalAdmin(),
|
globalAdmin(),
|
||||||
hsOfficePersonAdmin(newRelAnchor)]
|
hsOfficePersonAdmin(newRelAnchor)]
|
||||||
@ -53,14 +53,14 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipAdmin(NEW),
|
hsOfficeRelationshipAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
-- the tenant role for those related users who can view the data
|
-- the tenant role for those related users who can view the data
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeRelationshipTenant,
|
hsOfficeRelationshipTenant,
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeRelationshipAdmin(NEW),
|
hsOfficeRelationshipAdmin(NEW),
|
||||||
hsOfficePersonAdmin(newRelAnchor),
|
hsOfficePersonAdmin(newRelAnchor),
|
||||||
|
@ -48,13 +48,13 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePartnerOwner(NEW),
|
hsOfficePartnerOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePartnerAdmin(NEW),
|
hsOfficePartnerAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficePartnerOwner(NEW)],
|
hsOfficePartnerOwner(NEW)],
|
||||||
outgoingSubRoles => array[
|
outgoingSubRoles => array[
|
||||||
@ -84,7 +84,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficePartnerGuest(NEW),
|
hsOfficePartnerGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
|
incomingSuperRoles => array[hsOfficePartnerTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -99,12 +99,12 @@ begin
|
|||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficePartnerOwner(NEW), 'fail'),
|
getRoleId(hsOfficePartnerOwner(NEW), 'fail'),
|
||||||
createPermissions(NEW.detailsUuid, array ['*'])
|
createPermissions(NEW.detailsUuid, array ['DELETE'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
getRoleId(hsOfficePartnerAdmin(NEW), 'fail'),
|
getRoleId(hsOfficePartnerAdmin(NEW), 'fail'),
|
||||||
createPermissions(NEW.detailsUuid, array ['edit'])
|
createPermissions(NEW.detailsUuid, array ['UPDATE'])
|
||||||
);
|
);
|
||||||
|
|
||||||
call grantPermissionsToRole(
|
call grantPermissionsToRole(
|
||||||
@ -112,7 +112,7 @@ begin
|
|||||||
-- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
|
-- Do NOT grant view permission on partner-details to hsOfficePartnerTENANT!
|
||||||
-- Otherwise package-admins etc. would be able to read the data.
|
-- Otherwise package-admins etc. would be able to read the data.
|
||||||
getRoleId(hsOfficePartnerAgent(NEW), 'fail'),
|
getRoleId(hsOfficePartnerAgent(NEW), 'fail'),
|
||||||
createPermissions(NEW.detailsUuid, array ['view'])
|
createPermissions(NEW.detailsUuid, array ['SELECT'])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,9 +7,6 @@ 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:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
@ -38,7 +35,7 @@ call generateRbacRestrictedView('hs_office_partner_details',
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-partner-details-rbac-NEW-CONTACT:1 endDelimiter:--//
|
--changeset hs-office-partner-details-rbac-NEW-PARTNER-DETAILS:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
|
Creates a global permission for new-partner-details and assigns it to the hostsharing admins role.
|
||||||
|
@ -33,7 +33,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeBankAccountOwner(NEW),
|
hsOfficeBankAccountOwner(NEW),
|
||||||
permissions => array['delete'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()],
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
grantedByRole => globalAdmin()
|
grantedByRole => globalAdmin()
|
||||||
@ -51,7 +51,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeBankAccountGuest(NEW),
|
hsOfficeBankAccountGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
|
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,13 +41,13 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeSepaMandateOwner(NEW),
|
hsOfficeSepaMandateOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeSepaMandateAdmin(NEW),
|
hsOfficeSepaMandateAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
|
incomingSuperRoles => array[hsOfficeSepaMandateOwner(NEW)],
|
||||||
outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
|
outgoingSubRoles => array[hsOfficeBankAccountTenant(newHsOfficeBankAccount)]
|
||||||
);
|
);
|
||||||
@ -66,7 +66,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeSepaMandateGuest(NEW),
|
hsOfficeSepaMandateGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
|
incomingSuperRoles => array[hsOfficeSepaMandateTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeDebitorOwner(NEW),
|
hsOfficeDebitorOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()],
|
incomingSuperRoles => array[globalAdmin()],
|
||||||
userUuids => array[currentUserUuid()],
|
userUuids => array[currentUserUuid()],
|
||||||
grantedByRole => globalAdmin()
|
grantedByRole => globalAdmin()
|
||||||
@ -57,7 +57,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeDebitorAdmin(NEW),
|
hsOfficeDebitorAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeDebitorGuest(NEW),
|
hsOfficeDebitorGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[
|
incomingSuperRoles => array[
|
||||||
hsOfficeDebitorTenant(NEW)]
|
hsOfficeDebitorTenant(NEW)]
|
||||||
);
|
);
|
||||||
|
@ -41,13 +41,13 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipOwner(NEW),
|
hsOfficeMembershipOwner(NEW),
|
||||||
permissions => array['*'],
|
permissions => array['DELETE'],
|
||||||
incomingSuperRoles => array[globalAdmin()]
|
incomingSuperRoles => array[globalAdmin()]
|
||||||
);
|
);
|
||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipAdmin(NEW),
|
hsOfficeMembershipAdmin(NEW),
|
||||||
permissions => array['edit'],
|
permissions => array['UPDATE'],
|
||||||
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
|
incomingSuperRoles => array[hsOfficeMembershipOwner(NEW)]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ begin
|
|||||||
|
|
||||||
perform createRoleWithGrants(
|
perform createRoleWithGrants(
|
||||||
hsOfficeMembershipGuest(NEW),
|
hsOfficeMembershipGuest(NEW),
|
||||||
permissions => array['view'],
|
permissions => array['SELECT'],
|
||||||
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
incomingSuperRoles => array[hsOfficeMembershipTenant(NEW), hsOfficePartnerTenant(newHsOfficePartner), hsOfficeDebitorTenant(newHsOfficeDebitor)]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ 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), 'fail'),
|
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
|
||||||
createPermissions(NEW.uuid, array ['view'])
|
createPermissions(NEW.uuid, array ['SELECT'])
|
||||||
);
|
);
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -43,7 +43,7 @@ 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), 'fail'),
|
getRoleId(hsOfficeMembershipTenant(newHsOfficeMembership), 'fail'),
|
||||||
createPermissions(NEW.uuid, array ['view'])
|
createPermissions(NEW.uuid, array ['SELECT'])
|
||||||
);
|
);
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.data.annotation.Immutable;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "rbacobject") // TODO: create view rbacobject_ev
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Immutable
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class RawRbacObjectEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private UUID uuid;
|
||||||
|
|
||||||
|
@Column(name="objecttable")
|
||||||
|
private String objectTable;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static List<String> objectDisplaysOf(@NotNull final List<RawRbacObjectEntity> roles) {
|
||||||
|
return roles.stream().map(e -> e.objectTable+ "#" + e.uuid).sorted().toList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package net.hostsharing.hsadminng.rbac.rbacrole;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface RawRbacObjectRepository extends Repository<RawRbacObjectEntity, UUID> {
|
||||||
|
|
||||||
|
List<RawRbacObjectEntity> findAll();
|
||||||
|
}
|
@ -288,7 +288,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "view"))
|
hasEntry("op", "select"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
@ -298,7 +298,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "*"))
|
hasEntry("op", "delete"))
|
||||||
))
|
))
|
||||||
.body("size()", is(7));
|
.body("size()", is(7));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
@ -323,7 +323,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "view"))
|
hasEntry("op", "select"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
@ -333,7 +333,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "*"))
|
hasEntry("op", "delete"))
|
||||||
))
|
))
|
||||||
.body("size()", is(7));
|
.body("size()", is(7));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
@ -357,7 +357,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_customer#yyy.tenant"),
|
hasEntry("roleName", "test_customer#yyy.tenant"),
|
||||||
hasEntry("op", "view"))
|
hasEntry("op", "select"))
|
||||||
))
|
))
|
||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
@ -367,7 +367,7 @@ class RbacUserControllerAcceptanceTest {
|
|||||||
.body("", hasItem(
|
.body("", hasItem(
|
||||||
allOf(
|
allOf(
|
||||||
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
hasEntry("roleName", "test_domain#yyy00-aaaa.owner"),
|
||||||
hasEntry("op", "*"))
|
hasEntry("op", "delete"))
|
||||||
))
|
))
|
||||||
.body("size()", is(7));
|
.body("size()", is(7));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
@ -29,8 +29,9 @@ class TestCustomerEntityTest {
|
|||||||
subgraph customer:permissions[ ]
|
subgraph customer:permissions[ ]
|
||||||
style customer:permissions fill:#dd4901,stroke:white
|
style customer:permissions fill:#dd4901,stroke:white
|
||||||
|
|
||||||
perm:customer:*{{customer:*}}
|
perm:customer:delete{{customer:delete}}
|
||||||
perm:customer:view{{customer:view}}
|
perm:customer:update{{customer:update}}
|
||||||
|
perm:customer:select{{customer:select}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -43,9 +44,9 @@ class TestCustomerEntityTest {
|
|||||||
role:customer:admin ==> role:customer:tenant
|
role:customer:admin ==> role:customer:tenant
|
||||||
|
|
||||||
%% granting permissions to roles
|
%% granting permissions to roles
|
||||||
role:customer:owner ==> perm:customer:*
|
role:customer:owner ==> perm:customer:delete
|
||||||
role:customer:admin ==> perm:customer:add-package
|
role:customer:admin ==> perm:customer:add-package
|
||||||
role:customer:tenant ==> perm:customer:view
|
role:customer:tenant ==> perm:customer:select
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ class TestPackageEntityTest {
|
|||||||
style package:permissions fill:#dd4901,stroke:white
|
style package:permissions fill:#dd4901,stroke:white
|
||||||
|
|
||||||
perm:package:insert{{package:insert}}
|
perm:package:insert{{package:insert}}
|
||||||
perm:package:*{{package:*}}
|
perm:package:delete{{package:delete}}
|
||||||
perm:package:edit{{package:edit}}
|
perm:package:update{{package:update}}
|
||||||
perm:package:view{{package:view}}
|
perm:package:select{{package:select}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -63,9 +63,9 @@ class TestPackageEntityTest {
|
|||||||
|
|
||||||
%% granting permissions to roles
|
%% granting permissions to roles
|
||||||
role:customer:admin ==> perm:package:insert
|
role:customer:admin ==> perm:package:insert
|
||||||
role:package:owner ==> perm:package:*
|
role:package:owner ==> perm:package:delete
|
||||||
role:package:owner ==> perm:package:edit
|
role:package:owner ==> perm:package:update
|
||||||
role:package:tenant ==> perm:package:view
|
role:package:tenant ==> perm:package:select
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ class TestPackageRepositoryIntegrationTest {
|
|||||||
class OptimisticLocking {
|
class OptimisticLocking {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportsOptimisticLocking() throws InterruptedException {
|
public void supportsOptimisticLocking() {
|
||||||
// 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);
|
||||||
|
Loading…
Reference in New Issue
Block a user