Merge branch 'master' into TP-20240927-importfixes
This commit is contained in:
commit
bec4439fb0
@ -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: rbactest.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
|
||||
|
||||
|
36
bin/git-pull-and-if-origin-changed-run-tests
Executable file
36
bin/git-pull-and-if-origin-changed-run-tests
Executable 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
|
@ -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.
|
||||
|
@ -206,14 +206,14 @@ Limit (cost=6549.08..6549.35 rows=54 width=16)
|
||||
|
||||
```SQL
|
||||
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 hs_office_relation_rv hore1_0
|
||||
LEFT JOIN hs_office_person_rv a1_0 ON a1_0.uuid=hore1_0.anchoruuid
|
||||
LEFT JOIN hs_office_contact_rv c1_0 ON c1_0.uuid=hore1_0.contactuuid
|
||||
LEFT JOIN hs_office_person_rv h1_0 ON h1_0.uuid=hore1_0.holderuuid
|
||||
FROM hs_office.relation_rv hore1_0
|
||||
LEFT JOIN hs_office.person_rv a1_0 ON a1_0.uuid=hore1_0.anchoruuid
|
||||
LEFT JOIN hs_office.contact_rv c1_0 ON c1_0.uuid=hore1_0.contactuuid
|
||||
LEFT JOIN hs_office.person_rv h1_0 ON h1_0.uuid=hore1_0.holderuuid
|
||||
WHERE hore1_0.uuid=$1
|
||||
```
|
||||
|
||||
That query on the `hs_office_relation_rv`-table joins the three references anchor-person, holder-person and contact.
|
||||
That query on the `hs_office.relation_rv`-table joins the three references anchor-person, holder-person and contact.
|
||||
|
||||
|
||||
### Total-Query-Time > Total-Import-Runtime
|
||||
@ -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:
|
||||
@ -270,16 +270,16 @@ At this point, the import took 21mins with these statistics:
|
||||
|
||||
| query | calls | total_m | mean_ms |
|
||||
|-------|-------|---------|---------|
|
||||
| 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 |
|
||||
| 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 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 |
|
||||
| insert into hs_office_relation (uuid, version, anchoruuid, holderuuid, contactuuid, type, mark) values (new.uuid, new. version, new. anchoruuid, new. holderuuid, new. contactuuid, new. type, new. mark) returning * | 1261 | 0 | 9 |
|
||||
| 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 |
|
||||
| insert into hs_office.relation (uuid, version, anchoruuid, holderuuid, contactuuid, type, mark) values (new.uuid, new. version, new. anchoruuid, new. holderuuid, new. contactuuid, new. type, new. mark) returning * | 1261 | 0 | 9 |
|
||||
| call buildRbacSystemForHsOfficeRelation(NEW) | 1276 | 0 | 8 |
|
||||
| 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) | 47540 | 0 | 0 |
|
||||
| insert into RbacGrants (grantedByTriggerOf, ascendantuuid, descendantUuid, assumed) values (currentTriggerObjectUuid(), superRoleId, subRoleId, doAssume) on conflict do nothing" | 40472 | 0 | 0 |
|
||||
@ -294,17 +294,17 @@ We changed these mappings from `EAGER` (default) to `LAZY` to `@ManyToOne(fetch
|
||||
:::small
|
||||
| query | calls | total (min) | mean (ms) |
|
||||
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|-------------|----------|
|
||||
| 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 |
|
||||
| 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 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 |
|
||||
| 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) | 47538 | 0 | 0 |
|
||||
insert into public.hs_office_relation_rv (anchoruuid,contactuuid,holderuuid,mark,type,version,uuid) values ($1,$2,$3,$4,$5,$6,$7) | 1261 | 0 | 8 |
|
||||
| insert into hs_office_relation (uuid, version, anchoruuid, holderuuid, contactuuid, type, mark) values (new.uuid, new. version, new. anchoruuid, new. holderuuid, new. contactuuid, new. type, new. mark) returning * | 1261 | 0 | 8 |
|
||||
insert into public.hs_office.relation_rv (anchoruuid,contactuuid,holderuuid,mark,type,version,uuid) values ($1,$2,$3,$4,$5,$6,$7) | 1261 | 0 | 8 |
|
||||
| insert into hs_office.relation (uuid, version, anchoruuid, holderuuid, contactuuid, type, mark) values (new.uuid, new. version, new. anchoruuid, new. holderuuid, new. contactuuid, new. type, new. mark) returning * | 1261 | 0 | 8 |
|
||||
| call buildRbacSystemForHsOfficeRelation(NEW) | 1276 | 0 | 7 |
|
||||
| insert into public.hs_booking_item_rv (caption,parentitemuuid,projectuuid,resources,type,validity,version,uuid) values ($1,$2,$3,$4,$5,$6,$7,$8) | 926 | 0 | 7 |
|
||||
| insert into hs_booking_item (resources, version, projectuuid, type, parentitemuuid, validity, uuid, caption) values (new.resources, new. version, new. projectuuid, new. type, new. parentitemuuid, new. validity, new. uuid, new. caption) returning * | 926 | 0 | 7 |
|
||||
@ -331,13 +331,13 @@ Now, the longest running queries are these:
|
||||
| No.| calls | total_m | mean_ms | query |
|
||||
|---:|---------|--------:|--------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| 9 | 13.144 | 1 | 8 | SELECT createRoleWithGrants( hsHostingAssetTENANT(NEW), permissions => array[$7], incomingSuperRoles => array[ hsHostingAssetAGENT(NEW), hsOfficeContactADMIN(newAlarmContact)], outgoingSubRoles => array[ hsBookingItemTENANT(newBookingItem), hsHostingAssetTENANT(newParentAsset)] ) |
|
||||
| 10 | 13.144 | 1 | 5 | SELECT createRoleWithGrants( hsHostingAssetADMIN(NEW), permissions => array[$7], incomingSuperRoles => array[ hsBookingItemAGENT(newBookingItem), hsHostingAssetAGENT(newParentAsset), hsHostingAssetOWNER(NEW)] ) |
|
||||
|
||||
@ -345,7 +345,7 @@ That the `INSERT into hs_hosting_asset` (No. 1) takes up the most time, seems to
|
||||
|
||||
It seems that the trigger effects (eg. No. 3 and No. 4) are included in the measure for the causing INSERT, otherwise summing up the totals would exceed the actual total time of the whole import. And it was to be expected that building the RBAC rules for new business objects takes most of the time.
|
||||
|
||||
In production, the `SELECT ... FROM hs_office_relation_rv` (No. 2) with about 0.5 seconds could still be a problem. But once we apply the improvements from the hosting asset area also to the office area, this should not be a problem for the import anymore.
|
||||
In production, the `SELECT ... FROM hs_office.relation_rv` (No. 2) with about 0.5 seconds could still be a problem. But once we apply the improvements from the hosting asset area also to the office area, this should not be a problem for the import anymore.
|
||||
|
||||
|
||||
## Further Options To Explore
|
||||
@ -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`,
|
||||
|
56
doc/rbac.md
56
doc/rbac.md
@ -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.
|
||||
|
@ -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';
|
||||
|
@ -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('rbactest.package#aaa00:OWNER'));
|
||||
select rbac.isGranted(rbac.findRoleId('rbactest.package#aaa00:OWNER'), rbac.findRoleId('administrators'));
|
||||
-- call rbac.grantRoleToRole(findRoleId('rbactest.package#aaa00:OWNER'), findRoleId('administrators'));
|
||||
-- call rbac.grantRoleToRole(findRoleId('administrators'), findRoleId('rbactest.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 rbactest.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;
|
||||
|
@ -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('rbactest.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 rbactest.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 rbactest.customer WHERE rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('rbactest.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 rbactest.customer ENABLE ROW LEVEL SECURITY;
|
||||
DROP VIEW IF EXISTS cust_view;
|
||||
CREATE OR REPLACE VIEW cust_view AS
|
||||
SELECT *
|
||||
FROM customer;
|
||||
FROM rbactest.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 rbactest.customer AS c
|
||||
JOIN rbac.queryAllPermissionsOfSubjectId(rbac.currentSubjectUuid()) AS p
|
||||
ON p.objectTable='rbactest.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='rbactest.customer');
|
||||
|
||||
call grantRoleToUser(findRoleId('test_customer#aaa:ADMIN'), findRbacUser('aaaaouq@example.com'));
|
||||
call rbac.grantRoleToUser(rbac.findRoleId('rbactest.customer#aaa:ADMIN'), rbac.findRbacSubject('aaaaouq@example.com'));
|
||||
|
||||
select queryAllPermissionsOfSubjectId(findRbacUser('aaaaouq@example.com'));
|
||||
select rbac.queryAllPermissionsOfSubjectId(findRbacSubject('aaaaouq@example.com'));
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -4,9 +4,9 @@ import lombok.Getter;
|
||||
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.hs.booking.project.HsBookingProjectRbacEntity;
|
||||
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,10 +46,10 @@ 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(),
|
||||
.importEntityAlias("project", HsBookingProjectRbacEntity.class, usingDefaultCase(),
|
||||
dependsOnColumn("projectUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
NULLABLE)
|
||||
@ -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 {
|
||||
|
@ -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
|
||||
@ -67,11 +68,11 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
return rbacViewFor("project", HsBookingProject.class)
|
||||
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
|
||||
JOIN hs_office.debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.expression("caption"))
|
||||
.withUpdatableColumns("version", "caption")
|
||||
@ -85,13 +86,13 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
|
||||
dependsOnColumn("debitorUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
FROM hs_office_relation debitorRel
|
||||
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = ${REF}.debitorUuid
|
||||
"""),
|
||||
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 {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,9 +42,9 @@ 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
|
||||
JOIN hs_office.debitor_iv debitorIV ON debitorIV.uuid = bookingProject.debitorUuid
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.expression("caption"))
|
||||
.withUpdatableColumns("version", "caption")
|
||||
@ -57,13 +58,13 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
|
||||
dependsOnColumn("debitorUuid"),
|
||||
fetchedBySql("""
|
||||
SELECT ${columns}
|
||||
FROM hs_office_relation debitorRel
|
||||
JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = ${REF}.debitorUuid
|
||||
"""),
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -4,33 +4,33 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRbacEntity;
|
||||
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;
|
||||
|
||||