introduce separate database-schemas base+rbac (#103)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Co-authored-by: Michael Hönnig <michael@hoennig.de>
Reviewed-on: #103
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig 2024-09-16 15:36:37 +02:00
parent 80d79de5f4
commit 1eed0e9b21
287 changed files with 3194 additions and 3454 deletions

View File

@ -77,17 +77,17 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
# the following command should return a JSON array with just all customers:
curl \
-H 'current-user: superuser-alex@hostsharing.net' \
-H 'current-subject: superuser-alex@hostsharing.net' \
http://localhost:8080/api/test/customers
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
curl \
-H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
-H 'current-subject: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
http://localhost:8080/api/test/packages
# add a new customer
curl \
-H 'current-user: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
-H 'current-subject: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
-d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
-X POST http://localhost:8080/api/test/customers

View File

@ -0,0 +1,36 @@
#!/bin/bash
# get the current branch name
BRANCH=$(git rev-parse --abbrev-ref HEAD)
while true; do
# get the latest commit hashes from origin and local
git fetch origin
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/$BRANCH)
# check if the local branch differs from the remote branch
if [ "$LOCAL" != "$REMOTE" ]; then
echo "local $LOCAL differs from remote $REMOTE => pulling changes from origin"
git pull origin $BRANCH
# run the command
echo "Running ./gradlew test"
source .aliases # only variables, aliases are not expanded in scripts
./gradlew test
fi
# wait 10s with a little animation
echo -e -n " waiting for changes (/) ..."
sleep 2
echo -e -n "\r\033[K waiting for changes (-) ..."
sleep 2
echo -e -n "\r\033[K waiting for changes (\) ..."
sleep 2
echo -e -n "\r\033[K waiting for changes (|) ..."
sleep 2
echo -e -n "\r\033[K waiting for changes ( ) ... "
sleep 2
echo -e -n "\r\033[K"
done

View File

@ -14,9 +14,9 @@ The core problem here is, that in our RBAC system, determining the permissions o
### Technical Background
The session variable `hsadminng.currentUser` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user).
The session variable `hsadminng.currentSubject` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user).
Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing user has a given permission (e.g. 'view').
Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing subject has a given permission (e.g. 'view').
Given is also a stored function `queryAllPermissionsOfSubjectId` which returns the flattened view to all permissions assigned to the given accessing user.
@ -38,7 +38,7 @@ In this solution, the database ignores row level visibility and returns all rows
Very flexible access, programmatic, rules could be implemented.
The role-hierarchy and permissions for currently logged-in users user could be cached in the backend.
The role-hierarchy and permissions for current subjects (e.g. logged-in users) could be cached in the backend.
The access logic can be tested in pure Java unit tests.
@ -74,11 +74,11 @@ For restricted DB-users, which are used by the backend, access to rows is filter
FOR SELECT
TO restricted
USING (
isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid())
rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('customer', id, 'view'), currentSubjectUuid())
);
SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentSubject TO 'alex@example.com';
SELECT * from customer; -- will only return visible rows
#### Advantages
@ -101,10 +101,10 @@ We are bound to PostgreSQL, including integration tests and testing the RBAC sys
CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view
DO INSTEAD
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid());
SELECT * FROM customer WHERE rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('customer', id, 'view'), currentSubjectUuid());
SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentSubject TO 'alex@example.com';
SELECT * from customer; -- will only return visible rows
#### Advantages
@ -130,12 +130,12 @@ We do not access the tables directly from the backend, but via views which join
CREATE OR REPLACE VIEW cust_view AS
SELECT c.id, c.reference, c.prefix
FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
JOIN queryAllPermissionsOfSubjectId(currentSubjectUuid()) AS p
ON p.tableName='customer' AND p.rowId=c.id AND p.op='view';
GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentSubject TO 'alex@example.com';
SELECT * from cust_view; -- will only return visible rows
Alternatively the JOIN could also be applied in a "ON SELECT DO INSTEAD"-RULE, if there is any advantage for later features.

View File

@ -239,7 +239,7 @@ This did not improve the performance.
We were suspicious about the sequential scan over all `rbacpermission` rows which was done by PostgreSQL to execute a HashJoin strategy. Turning off that strategy by
```SQL
ALTER FUNCTION queryAccessibleObjectUuidsOfSubjectIds SET enable_hashjoin = off;
ALTER FUNCTION rbac.queryAccessibleObjectUuidsOfSubjectIds SET enable_hashjoin = off;
```
did not improve the performance though. The HashJoin was actually still applied, but no full table scan anymore:
@ -273,9 +273,9 @@ At this point, the import took 21mins with these statistics:
| select hore1_0.uuid,a1_0.uuid,a1_0.familyname,a1_0.givenname,a1_0.persontype,a1_0.salutation,a1_0.title,a1_0.tradename,a1_0.version,c1_0.uuid,c1_0.caption,c1_0.emailaddresses,c1_0.phonenumbers,c1_0.postaladdress, c1_0.version,h1_0.uuid,h1_0.familyname,h1_0.givenname,h1_0.persontype,h1_0.salutation,h1_0.title,h1_0.tradename,h1_0.version,hore1_0.mark,hore1_0.type,hore1_0.version from public.hs_office_relation_rv hore1_0 left join public.hs_office_person_rv a1_0 on a1_0.uuid=hore1_0.anchoruuid left join public.hs_office_contact_rv c1_0 on c1_0.uuid=hore1_0.contactuuid left join public.hs_office_person_rv h1_0 on h1_0.uuid=hore1_0.holderuuid where hore1_0.uuid=$1 | 517 | 11 | 1282 |
| select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from public.hs_office_person_rv hope1_0 where hope1_0.uuid=$1 | 973 | 4 | 254 |
| select hoce1_0.uuid,hoce1_0.caption,hoce1_0.emailaddresses,hoce1_0.phonenumbers,hoce1_0.postaladdress,hoce1_0.version from public.hs_office_contact_rv hoce1_0 where hoce1_0.uuid=$1 | 973 | 4 | 253 |
| call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| call rbac.grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| call buildRbacSystemForHsHostingAsset(NEW) | 2258 | 0 | 7 |
| select * from isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 |
| select * from rbac.isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 |
| insert into public.hs_hosting_asset_rv (alarmcontactuuid,assignedtoassetuuid,bookingitemuuid,caption,config,identifier,parentassetuuid,type,version,uuid) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) | 2207 | 0 | 7 |
| insert into hs_hosting_asset (alarmcontactuuid, version, bookingitemuuid, type, parentassetuuid, assignedtoassetuuid, config, uuid, identifier, caption) values (new.alarmcontactuuid, new. version, new. bookingitemuuid, new. type, new. parentassetuuid, new. assignedtoassetuuid, new. config, new. uuid, new. identifier, new. caption) returning * | 2207 | 0 | 7 |
| insert into public.hs_office_relation_rv (anchoruuid,contactuuid,holderuuid,mark,type,version,uuid) values ($1,$2,$3,$4,$5,$6,$7) | 1261 | 0 | 9 |
@ -297,8 +297,8 @@ We changed these mappings from `EAGER` (default) to `LAZY` to `@ManyToOne(fetch
| select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from public.hs_office_person_rv hope1_0 where hope1_0.uuid=$1 | 1015 | 4 | 238 |
| select hore1_0.uuid,hore1_0.anchoruuid,hore1_0.contactuuid,hore1_0.holderuuid,hore1_0.mark,hore1_0.type,hore1_0.version from public.hs_office_relation_rv hore1_0 where hore1_0.uuid=$1 | 517 | 4 | 439 |
| select hoce1_0.uuid,hoce1_0.caption,hoce1_0.emailaddresses,hoce1_0.phonenumbers,hoce1_0.postaladdress,hoce1_0.version from public.hs_office_contact_rv hoce1_0 where hoce1_0.uuid=$1 | 497 | 2 | 213 |
| call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| select * from isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 |
| call rbac.grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| select * from rbac.isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 |
| call buildRbacSystemForHsHostingAsset(NEW) | 2258 | 0 | 7 |
| insert into public.hs_hosting_asset_rv (alarmcontactuuid,assignedtoassetuuid,bookingitemuuid,caption,config,identifier,parentassetuuid,type,version,uuid) values ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) | 2207 | 0 | 7 |
| insert into hs_hosting_asset (alarmcontactuuid, version, bookingitemuuid, type, parentassetuuid, assignedtoassetuuid, config, uuid, identifier, caption) values (new.alarmcontactuuid, new. version, new. bookingitemuuid, new. type, new. parentassetuuid, new. assignedtoassetuuid, new. config, new. uuid, new. identifier, new. caption) returning * | 2207 | 0 | 7 |
@ -333,8 +333,8 @@ Now, the longest running queries are these:
| 1 | 13.093 | 4 | 21 | insert into hs_hosting_asset( uuid, type, bookingitemuuid, parentassetuuid, assignedtoassetuuid, alarmcontactuuid, identifier, caption, config, version) values ( $1, $2, $3, $4, $5, $6, $7, $8, cast($9 as jsonb), $10) |
| 2 | 517 | 4 | 502 | select hore1_0.uuid,hore1_0.anchoruuid,hore1_0.contactuuid,hore1_0.holderuuid,hore1_0.mark,hore1_0.type,hore1_0.version from public.hs_office_relation_rv hore1_0 where hore1_0.uuid=$1 |
| 3 | 13.144 | 4 | 21 | call buildRbacSystemForHsHostingAsset(NEW) |
| 4 | 96.632 | 3 | 2 | call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) |
| 5 | 120.815 | 3 | 2 | select * from isGranted(array[granteeId], grantedId) |
| 4 | 96.632 | 3 | 2 | call rbac.grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) |
| 5 | 120.815 | 3 | 2 | select * from rbac.isGranted(array[granteeId], grantedId) |
| 6 | 123.740 | 3 | 2 | with recursive grants as ( select descendantUuid, ascendantUuid from RbacGrants where descendantUuid = grantedId union all select "grant".descendantUuid, "grant".ascendantUuid from RbacGrants "grant" inner join grants recur on recur.ascendantUuid = "grant".descendantUuid ) select exists ( select $3 from grants where ascendantUuid = any(granteeIds) ) or grantedId = any(granteeIds) |
| 7 | 497 | 2 | 259 | select hoce1_0.uuid,hoce1_0.caption,hoce1_0.emailaddresses,hoce1_0.phonenumbers,hoce1_0.postaladdress,hoce1_0.version from public.hs_office_contact_rv hoce1_0 where hoce1_0.uuid=$1 |
| 8 | 497 | 2 | 255 | select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from public.hs_office_person_rv hope1_0 where hope1_0.uuid=$1 |
@ -392,9 +392,9 @@ We found some solution approaches:
3. Inverting the recursion of the CTE-query, combined with the type condition.
Instead of starting the recursion with `currentsubjectsuuids()`,
Instead of starting the recursion with `currentSubjectOrAssumedRolesUuids()`,
we could start it with the target table name and row-type,
then recurse down to the `currentsubjectsuuids()`.
then recurse down to the `currentSubjectOrAssumedRolesUuids()`.
In the end, we need the object UUIDs, though.
But if we start with the join of `rbacObject` with `rbacPermission`,

View File

@ -29,7 +29,7 @@ skinparam linetype ortho
package RBAC {
' forward declarations
entity RbacUser
entity RbacSubject
together {
@ -37,8 +37,8 @@ package RBAC {
entity RbacPermission
RbacUser -[hidden]> RbacRole
RbacRole -[hidden]> RbacUser
RbacSubject -[hidden]> RbacRole
RbacRole -[hidden]> RbacSubject
}
together {
@ -57,11 +57,11 @@ package RBAC {
RbacGrant o-u-> RbacReference
enum RbacReferenceType {
RbacUser
RbacSubject
RbacRole
RbacPermission
}
RbacReferenceType ..> RbacUser
RbacReferenceType ..> RbacSubject
RbacReferenceType ..> RbacRole
RbacReferenceType ..> RbacPermission
@ -71,12 +71,12 @@ package RBAC {
type : RbacReferenceType
}
RbacReference o--> RbacReferenceType
entity RbacUser {
entity RbacSubject {
*uuid : uuid <<generated>>
--
name : varchar
}
RbacUser o-- RbacReference
RbacSubject o-- RbacReference
entity RbacRole {
*uuid : uuid(RbacReference)
@ -143,20 +143,20 @@ The primary key of the *RbacReference* and its referred object is always identic
#### RbacReferenceType
The enum *RbacReferenceType* describes the type of reference.
It's only needed to make it easier to find the referred object in *RbacUser*, *RbacRole* or *RbacPermission*.
It's only needed to make it easier to find the referred object in *RbacSubject*, *RbacRole* or *RbacPermission*.
#### RbacUser
#### RbacSubject
An *RbacUser* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address).
An *RbacSubject* is a type of RBAC-subject which references a login account outside this system, identified by a name (usually an email-address).
*RbacUser*s can be assigned to multiple *RbacRole*s, through which they can get permissions to *RbacObject*s.
*RbacSubject*s can be assigned to multiple *RbacRole*s, through which they can get permissions to *RbacObject*s.
The primary key of the *RbacUser* is identical to its related *RbacReference*.
The primary key of the *RbacSubject* is identical to its related *RbacReference*.
#### RbacRole
An *RbacRole* represents a collection of directly or indirectly assigned *RbacPermission*s.
Each *RbacRole* can be assigned to *RbacUser*s or to another *RbacRole*.
Each *RbacRole* can be assigned to *RbacSubject*s or to another *RbacRole*.
Both kinds of assignments are represented via *RbacGrant*.
@ -184,7 +184,7 @@ Only with this rule, the foreign key in *RbacPermission* can be defined as `NOT
#### RbacGrant
The *RbacGrant* entities represent the access-rights structure from *RbacUser*s via hierarchical *RbacRoles* down to *RbacPermission*s.
The *RbacGrant* entities represent the access-rights structure from *RbacSubject*s via hierarchical *RbacRoles* down to *RbacPermission*s.
The core SQL queries to determine access rights are all recursive queries on the *RbacGrant* table.
@ -284,7 +284,7 @@ hide circle
' use right-angled line routing
' skinparam linetype ortho
package RbacUsers {
package RbacSubjects {
object UserMike
object UserSuse
object UserPaul
@ -296,7 +296,7 @@ package RbacRoles {
object RoleCustXyz_Admin
object RolePackXyz00_Owner
}
RbacUsers -[hidden]> RbacRoles
RbacSubjects -[hidden]> RbacRoles
package RbacPermissions {
object PermCustXyz_SELECT
@ -364,10 +364,10 @@ This way, each user can only select the data they have 'SELECT'-permission for,
### Current User
The current use is taken from the session variable `hsadminng.currentUser` which contains the name of the user as stored in the
*RbacUser*s table. Example:
The current use is taken from the session variable `hsadminng.currentSubject` which contains the name of the user as stored in the
*RbacSubject*s table. Example:
SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';
SET LOCAL hsadminng.currentSubject = 'mike@hostsharing.net';
That user is also used for historicization and audit log, but which is a different topic.
@ -388,7 +388,7 @@ A full example is shown here:
BEGIN TRANSACTION;
SET SESSION SESSION AUTHORIZATION restricted;
SET LOCAL hsadminng.currentUser = 'mike@hostsharing.net';
SET LOCAL hsadminng.currentSubject = 'mike@hostsharing.net';
SET LOCAL hsadminng.assumedRoles = 'customer#aab:admin;customer#aac:admin';
SELECT c.prefix, p.name as "package", ema.localPart || '@' || dom.name as "email-address"
@ -605,8 +605,8 @@ Find the SQL script here: `28-hs-tests.sql`.
We have tested two variants of the query for the restricted view,
both utilizing a PostgreSQL function like this:
FUNCTION queryAccessibleObjectUuidsOfSubjectIds(
requiredOp RbacOp,
FUNCTION rbac.queryAccessibleObjectUuidsOfSubjectIds(
requiredOp rbac.RbacOp,
forObjectTable varchar,
subjectIds uuid[],
maxObjects integer = 16000)
@ -623,8 +623,8 @@ Let's have a look at the two view queries:
FROM customer AS target
WHERE target.uuid IN (
SELECT uuid
FROM queryAccessibleObjectUuidsOfSubjectIds(
'SELECT, 'customer', currentSubjectsUuids()));
FROM rbac.queryAccessibleObjectUuidsOfSubjectIds(
'SELECT, 'customer', currentSubjectOrAssumedRolesUuids()));
This view should be automatically updatable.
Where, for updates, we actually have to check for 'UPDATE' instead of 'SELECT' operation, which makes it a bit more complicated.
@ -641,8 +641,8 @@ Looks like the query optimizer needed some statistics to find the best path.
CREATE OR REPLACE VIEW customer_rv AS
SELECT DISTINCT target.*
FROM customer AS target
JOIN queryAccessibleObjectUuidsOfSubjectIds(
'SELECT, 'customer', currentSubjectsUuids()) AS allowedObjId
JOIN rbac.queryAccessibleObjectUuidsOfSubjectIds(
'SELECT, 'customer', currentSubjectOrAssumedRolesUuids()) AS allowedObjId
ON target.uuid = allowedObjId;
This view cannot is not updatable automatically,
@ -671,9 +671,9 @@ Access Control for business objects checked according to the assigned roles.
But we decided not to create such roles and permissions for the RBAC-Objects itself.
It would have overcomplicated the system and the necessary information can easily be added to the RBAC-Objects itself, mostly the `RbacGrant`s.
### RbacUser
### RbacSubject
Users can self-register, thus to create a new RbacUser entity, no login is required.
Users can self-register, thus to create a new RbacSubject entity, no login is required.
But such a user has no access-rights except viewing itself.
Users can view themselves.

View File

@ -24,13 +24,13 @@ delete from hs_hosting_asset where uuid='5aea68d2-3b55-464f-8362-b05c76c5a681'::
commit;
-- single version at point in time
-- set hsadminng.tx_history_txid to (select max(txid) from tx_context where txtimestamp<='2024-08-27 12:13:13.450821');
-- set hsadminng.tx_history_txid to (select max(txid) from base.tx_context where txtimestamp<='2024-08-27 12:13:13.450821');
set hsadminng.tx_history_txid to '';
set hsadminng.tx_history_timestamp to '2024-08-29 12:42';
-- all versions
select tx_history_txid(), txc.txtimestamp, txc.currentUser, txc.currentTask, haex.*
select base.tx_history_txid(), txc.txtimestamp, txc.currentSubject, txc.currentTask, haex.*
from hs_hosting_asset_ex haex
join tx_context txc on haex.txid=txc.txid
join base.tx_context txc on haex.txid=txc.txid
where haex.identifier = 'test@thi.example.org';
select uuid, version, type, identifier, caption from hs_hosting_asset_hv p where identifier = 'test@thi.example.org';

View File

@ -3,28 +3,28 @@
-- --------------------------------------------------------
select isGranted(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select isGranted(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
-- call grantRoleToRole(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
-- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select rbac.isGranted(rbac.findRoleId('administrators'), rbac.findRoleId('test.package#aaa00:OWNER'));
select rbac.isGranted(rbac.findRoleId('test.package#aaa00:OWNER'), rbac.findRoleId('administrators'));
-- call rbac.grantRoleToRole(findRoleId('test.package#aaa00:OWNER'), findRoleId('administrators'));
-- call rbac.grantRoleToRole(findRoleId('administrators'), findRoleId('test.package#aaa00:OWNER'));
select count(*)
FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacUser('superuser-fran@hostsharing.net'),
ARRAY(select uuid from customer where reference < 1100000));
FROM rbac.queryAllPermissionsOfSubjectIdForObjectUuids(rbac.findRbacSubject('superuser-fran@hostsharing.net'),
ARRAY(select uuid from test.customer where reference < 1100000));
select count(*)
FROM queryAllPermissionsOfSubjectId(findRbacUser('superuser-fran@hostsharing.net'));
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('superuser-fran@hostsharing.net'));
select *
FROM queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'));
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('alex@example.com'));
select *
FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com'));
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('rosa@example.com'));
select *
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
(SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
FROM rbac.queryAllRbacSubjectsWithPermissionsFor(rbac.findEffectivePermissionId('customer',
(SELECT uuid FROM rbac.RbacObject WHERE objectTable = 'customer' LIMIT 1),
'add-package'));
select *
FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
(SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
FROM rbac.queryAllRbacSubjectsWithPermissionsFor(rbac.findEffectivePermissionId('package',
(SELECT uuid FROM rbac.RbacObject WHERE objectTable = 'package' LIMIT 1),
'DELETE'));
DO LANGUAGE plpgsql
@ -33,13 +33,13 @@ $$
userId uuid;
result bool;
BEGIN
userId = findRbacUser('superuser-alex@hostsharing.net');
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId));
userId = rbac.findRbacSubject('superuser-alex@hostsharing.net');
result = (SELECT * FROM rbac.isPermissionGrantedToSubject(rbac.findPermissionId('package', 94928, 'add-package'), userId));
IF (result) THEN
RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
end if;
result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'SELECT'), userId));
result = (SELECT * FROM rbac.isPermissionGrantedToSubject(rbac.findPermissionId('package', 94928, 'SELECT'), userId));
IF (NOT result) THEN
RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
end if;

View File

@ -20,43 +20,43 @@ CREATE POLICY customer_policy ON customer
TO restricted
USING (
-- id=1000
isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid())
rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('test.customer', id, 'SELECT'), rbac.currentSubjectUuid())
);
SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentSubject TO 'alex@example.com';
SELECT * from customer;
-- access control via view-rule and isPermissionGrantedToSubject - way too slow (35 s 580 ms for 1 million rows)
SET SESSION SESSION AUTHORIZATION DEFAULT;
DROP VIEW cust_view;
CREATE VIEW cust_view AS
SELECT * FROM customer;
SELECT * FROM test.customer;
CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view
DO INSTEAD
SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('test_customer', id, 'SELECT'), currentUserUuid());
SELECT * FROM test.customer WHERE rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('test.customer', id, 'SELECT'), rbac.currentSubjectUuid());
SELECT * from cust_view LIMIT 10;
select queryAllPermissionsOfSubjectId(findRbacUser('superuser-alex@hostsharing.net'));
select rbac.queryAllPermissionsOfSubjectId(findRbacSubject('superuser-alex@hostsharing.net'));
-- access control via view-rule with join to recursive permissions - really fast (38ms for 1 million rows)
SET SESSION SESSION AUTHORIZATION DEFAULT;
ALTER TABLE customer ENABLE ROW LEVEL SECURITY;
ALTER TABLE test.customer ENABLE ROW LEVEL SECURITY;
DROP VIEW IF EXISTS cust_view;
CREATE OR REPLACE VIEW cust_view AS
SELECT *
FROM customer;
FROM test.customer;
CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view
DO INSTEAD
SELECT c.uuid, c.reference, c.prefix FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
ON p.objectTable='test_customer' AND p.objectUuid=c.uuid;
SELECT c.uuid, c.reference, c.prefix FROM test.customer AS c
JOIN rbac.queryAllPermissionsOfSubjectId(rbac.currentSubjectUuid()) AS p
ON p.objectTable='test.customer' AND p.objectUuid=c.uuid;
GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted;
SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentSubject TO 'alex@example.com';
SELECT * from cust_view;
@ -67,23 +67,23 @@ DROP VIEW IF EXISTS cust_view;
CREATE OR REPLACE VIEW cust_view AS
SELECT c.uuid, c.reference, c.prefix
FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
JOIN queryAllPermissionsOfSubjectId(rbac.currentSubjectUuid()) AS p
ON p.objectUuid=c.uuid;
GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted;
-- SET hsadminng.currentUser TO 'alex@example.com';
SET hsadminng.currentUser TO 'superuser-alex@hostsharing.net';
-- SET hsadminng.currentUser TO 'aaaaouq@example.com';
-- SET hsadminng.currentSubject TO 'alex@example.com';
SET hsadminng.currentSubject TO 'superuser-alex@hostsharing.net';
-- SET hsadminng.currentSubject TO 'aaaaouq@example.com';
SELECT * from cust_view where reference=1144150;
select rr.uuid, rr.type from RbacGrants g
join RbacReference RR on g.ascendantUuid = RR.uuid
select rr.uuid, rr.type from rbac.RbacGrants g
join rbac.RbacReference RR on g.ascendantUuid = RR.uuid
where g.descendantUuid in (
select uuid from queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'))
where objectTable='test_customer');
select uuid from rbac.queryAllPermissionsOfSubjectId(findRbacSubject('alex@example.com'))
where objectTable='test.customer');
call grantRoleToUser(findRoleId('test_customer#aaa:ADMIN'), findRbacUser('aaaaouq@example.com'));
call rbac.grantRoleToUser(rbac.findRoleId('test.customer#aaa:ADMIN'), rbac.findRbacSubject('aaaaouq@example.com'));
select queryAllPermissionsOfSubjectId(findRbacUser('aaaaouq@example.com'));
select rbac.queryAllPermissionsOfSubjectId(findRbacSubject('aaaaouq@example.com'));

View File

@ -1,6 +1,6 @@
-- just a permanent playground to explore optimization of the central recursive CTE query for RBAC
select * from hs_statistics_view;
select * from hs_statistics_v;
-- ========================================================
@ -17,7 +17,7 @@ with recursive
1 as level,
true
from rbacgrants
where (rbacgrants.ascendantuuid = any (currentsubjectsuuids()))
where (rbacgrants.ascendantuuid = any (rbac.currentSubjectOrAssumedRolesUuids()))
and rbacgrants.assumed
union all
select distinct g.descendantuuid,

View File

@ -38,53 +38,53 @@ public class Context {
private HttpServletRequest request;
@Transactional(propagation = MANDATORY)
public void define(final String currentUser) {
define(currentUser, null);
public void define(final String currentSubject) {
define(currentSubject, null);
}
@Transactional(propagation = MANDATORY)
public void define(final String currentUser, final String assumedRoles) {
define(toTask(request), toCurl(request), currentUser, assumedRoles);
public void define(final String currentSubject, final String assumedRoles) {
define(toTask(request), toCurl(request), currentSubject, assumedRoles);
}
@Transactional(propagation = MANDATORY)
public void define(
final String currentTask,
final String currentRequest,
final String currentUser,
final String currentSubject,
final String assumedRoles) {
final var query = em.createNativeQuery("""
call defineContext(
call base.defineContext(
cast(:currentTask as varchar(127)),
cast(:currentRequest as text),
cast(:currentUser as varchar(63)),
cast(:currentSubject as varchar(63)),
cast(:assumedRoles as varchar(1023)));
""");
query.setParameter("currentTask", shortenToMaxLength(currentTask, 127));
query.setParameter("currentRequest", currentRequest);
query.setParameter("currentUser", currentUser);
query.setParameter("currentSubject", currentSubject);
query.setParameter("assumedRoles", assumedRoles != null ? assumedRoles : "");
query.executeUpdate();
}
public String getCurrentTask() {
public String fetchCurrentTask() {
return (String) em.createNativeQuery("select current_setting('hsadminng.currentTask');").getSingleResult();
}
public String getCurrentUser() {
return String.valueOf(em.createNativeQuery("select currentUser()").getSingleResult());
public String fetchCurrentSubject() {
return String.valueOf(em.createNativeQuery("select base.currentSubject()").getSingleResult());
}
public UUID getCurrentUserUUid() {
return (UUID) em.createNativeQuery("select currentUserUUid()", UUID.class).getSingleResult();
public UUID fetchCurrentSubjectUuid() {
return (UUID) em.createNativeQuery("select rbac.currentSubjectUuid()", UUID.class).getSingleResult();
}
public String[] getAssumedRoles() {
return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
public String[] fetchAssumedRoles() {
return (String[]) em.createNativeQuery("select base.assumedRoles() as roles", String[].class).getSingleResult();
}
public UUID[] currentSubjectsUuids() {
return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
public UUID[] fetchCurrentSubjectOrAssumedRolesUuids() {
return (UUID[]) em.createNativeQuery("select rbac.currentSubjectOrAssumedRolesUuids() as uuids", UUID[].class).getSingleResult();
}
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {

View File

@ -14,7 +14,7 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;

View File

@ -41,10 +41,10 @@ public class HsBookingItemController implements HsBookingItemsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<List<HsBookingItemResource>> listBookingItemsByProjectUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID projectUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entities = bookingItemRepo.findAllByProjectUuid(projectUuid);
@ -55,11 +55,11 @@ public class HsBookingItemController implements HsBookingItemsApi {
@Override
@Transactional
public ResponseEntity<HsBookingItemResource> addBookingItem(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final HsBookingItemInsertResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -77,11 +77,11 @@ public class HsBookingItemController implements HsBookingItemsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<HsBookingItemResource> getBookingItemByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingItemUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = bookingItemRepo.findByUuid(bookingItemUuid);
result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading
@ -94,10 +94,10 @@ public class HsBookingItemController implements HsBookingItemsApi {
@Override
@Transactional
public ResponseEntity<Void> deleteBookingIemByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingItemUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = bookingItemRepo.deleteByUuid(bookingItemUuid);
return result == 0
@ -108,12 +108,12 @@ public class HsBookingItemController implements HsBookingItemsApi {
@Override
@Transactional
public ResponseEntity<HsBookingItemResource> patchBookingItem(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingItemUuid,
final HsBookingItemPatchResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var current = bookingItemRepo.findByUuid(bookingItemUuid).orElseThrow();

View File

@ -5,8 +5,8 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.generator.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
@ -15,19 +15,20 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_booking_item_rv")
@ -45,8 +46,8 @@ public class HsBookingItemRbacEntity extends HsBookingItem {
.withIdentityView(SQL.projection("caption"))
.withRestrictedViewOrderBy(SQL.expression("validity"))
.withUpdatableColumns("version", "caption", "validity", "resources")
.toRole("global", ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data?
.toRole("global", ADMIN).grantPermission(DELETE)
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data?
.toRole(GLOBAL, ADMIN).grantPermission(DELETE)
.importEntityAlias("project", HsBookingProject.class, usingDefaultCase(),
dependsOnColumn("projectUuid"),
@ -74,7 +75,7 @@ public class HsBookingItemRbacEntity extends HsBookingItem {
with.permission(SELECT);
})
.limitDiagramTo("bookingItem", "project", "global");
.limitDiagramTo("bookingItem", "project", "rbac.global");
}
public static void main(String[] args) throws IOException {

View File

@ -5,9 +5,9 @@ import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -17,15 +17,16 @@ import java.util.UUID;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@MappedSuperclass
@ -69,7 +70,7 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
public static RbacView rbac() {
return rbacViewFor("project", HsBookingProject.class)
.withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || cleanIdentifier(bookingProject.caption) as idName
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject
JOIN hs_office_debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
"""))
@ -91,7 +92,7 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
"""),
NOT_NULL)
.toRole("debitorRel", ADMIN).grantPermission(INSERT)
.toRole("global", ADMIN).grantPermission(DELETE)
.toRole(GLOBAL, ADMIN).grantPermission(DELETE)
.createRole(OWNER, (with) -> {
with.incomingSuperRole("debitorRel", AGENT).unassumed();
@ -105,7 +106,7 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
with.permission(SELECT);
})
.limitDiagramTo("project", "debitorRel", "global");
.limitDiagramTo("project", "debitorRel", "rbac.global");
}
public static void main(String[] args) throws IOException {

View File

@ -36,10 +36,10 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<List<HsBookingProjectResource>> listBookingProjectsByDebitorUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID debitorUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entities = bookingProjectRepo.findAllByDebitorUuid(debitorUuid);
@ -50,11 +50,11 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Override
@Transactional
public ResponseEntity<HsBookingProjectResource> addBookingProject(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final HsBookingProjectInsertResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entityToSave = mapper.map(body, HsBookingProjectRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -72,11 +72,11 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<HsBookingProjectResource> getBookingProjectByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingProjectUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = bookingProjectRepo.findByUuid(bookingProjectUuid);
return result
@ -88,10 +88,10 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Override
@Transactional
public ResponseEntity<Void> deleteBookingIemByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingProjectUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = bookingProjectRepo.deleteByUuid(bookingProjectUuid);
return result == 0
@ -102,12 +102,12 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
@Override
@Transactional
public ResponseEntity<HsBookingProjectResource> patchBookingProject(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bookingProjectUuid,
final HsBookingProjectPatchResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var current = bookingProjectRepo.findByUuid(bookingProjectUuid).orElseThrow();

View File

@ -6,29 +6,30 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.generator.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.io.IOException;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_booking_project_rv")
@ -41,7 +42,7 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
public static RbacView rbac() {
return rbacViewFor("project", HsBookingProjectRbacEntity.class)
.withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || cleanIdentifier(bookingProject.caption) as idName
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject
JOIN hs_office_debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
"""))
@ -63,7 +64,7 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
"""),
NOT_NULL)
.toRole("debitorRel", ADMIN).grantPermission(INSERT)
.toRole("global", ADMIN).grantPermission(DELETE)
.toRole(GLOBAL, ADMIN).grantPermission(DELETE)
.createRole(OWNER, (with) -> {
with.incomingSuperRole("debitorRel", AGENT).unassumed();
@ -77,7 +78,7 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
with.permission(SELECT);
})
.limitDiagramTo("project", "debitorRel", "global");
.limitDiagramTo("project", "debitorRel", "rbac.global");
}
public static void main(String[] args) throws IOException {

View File

@ -14,7 +14,7 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;

View File

@ -49,12 +49,12 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<List<HsHostingAssetResource>> listAssets(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID debitorUuid,
final UUID parentAssetUuid,
final HsHostingAssetTypeResource type) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entities = rbacAssetRepo.findAllByCriteria(debitorUuid, parentAssetUuid, HsHostingAssetType.of(type));
@ -66,11 +66,11 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
@Override
@Transactional
public ResponseEntity<HsHostingAssetResource> addAsset(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final HsHostingAssetInsertResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entity = mapper.map(body, HsHostingAssetRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -94,11 +94,11 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<HsHostingAssetResource> getAssetByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID assetUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = rbacAssetRepo.findByUuid(assetUuid);
return result
@ -110,10 +110,10 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
@Override
@Transactional
public ResponseEntity<Void> deleteAssetUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID assetUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var result = rbacAssetRepo.deleteByUuid(assetUuid);
return result == 0
@ -124,12 +124,12 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
@Override
@Transactional
public ResponseEntity<HsHostingAssetResource> patchAsset(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID assetUuid,
final HsHostingAssetPatchResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entity = rbacAssetRepo.findByUuid(assetUuid).orElseThrow();

View File

@ -6,31 +6,31 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.generator.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.GUEST;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity
@Table(name = "hs_hosting_asset_rv")
@ -106,7 +106,7 @@ public class HsHostingAssetRbacEntity extends HsHostingAsset {
"parentAsset",
"assignedToAsset",
"alarmContact",
"global");
"rbac.global");
}
public static void main(String[] args) throws IOException {

View File

@ -32,10 +32,10 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeBankAccountResource>> listBankAccounts(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final String holder) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
final var entities = bankAccountRepo.findByOptionalHolderLike(holder);
@ -46,11 +46,11 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
@Override
@Transactional
public ResponseEntity<HsOfficeBankAccountResource> addBankAccount(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final HsOfficeBankAccountInsertResource body) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);
IbanUtil.validate(body.getIban());
BicUtil.validate(body.getBic());
@ -72,11 +72,11 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
@Override
@Transactional(readOnly = true)
public ResponseEntity<HsOfficeBankAccountResource> getBankAccountByUuid(
final String currentUser,
final String currentSubject,
final String assumedRoles,
final UUID bankAccountUuid) {
context.define(currentUser, assumedRoles);
context.define(currentSubject, assumedRoles);