RBAC Diagram+PostgreSQL Generator #21

Merged
hsh-michaelhoennig merged 54 commits from experimental-rbacview-generator into master 2024-03-11 12:30:44 +01:00
43 changed files with 648 additions and 425 deletions
Showing only changes of commit 3cc5185551 - Show all commits

View File

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

View File

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

View File

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

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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");
} }
} }

View File

@ -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) {
getOptionalInsertSuperRole().ifPresent( superRoleDef -> {
plPgSql.writeLn(""" 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());
} }
} }

View File

@ -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
*/ */

View File

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

View File

@ -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);
}); });
} }

View File

@ -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);
}); });
} }

View File

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

View File

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

View File

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

View File

@ -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;
end if; else
grantedByRoleUuid := getRoleId(grantedByRole, 'fail'); grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
end if;
foreach userUuid in array userUuids foreach userUuid in array userUuids
loop loop
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid); call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)]
); );

View File

@ -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)]
); );

View File

@ -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),

View File

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

View File

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

View File

@ -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)]
); );

View File

@ -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)]
); );

View File

@ -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)]
); );

View File

@ -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)]
); );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
"""); """);
} }
} }

View File

@ -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
"""); """);
} }
} }

View File

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